다음과 같은 프로그램이 있다고 가정합니다.
#include "something.hpp"
int main(int argc, char* argv[]) {
some = new Something();
return 0;
}
다음 파일로 구성된 .so 라이브러리에 링크됩니다.
#include <iostream>
class Logger {
public:
Logger();
void log(char);
void set_name(char);
private:
char m_name;
};
#include "logger.hpp"
Logger::Logger() {}
void Logger::log(char msg) {
std::cout << this->m_name << " : " << msg;
}
void Logger::set_name(char name) {
this->m_name = name;
}
#include "logger.hpp"
class Something {
public:
Something();
};
#include "something.hpp"
Something::Something() {
logger->log("hello !");
}
현재 코드는 정의되지 않았 으므로 something.cpp
에서 실패 합니다. 을 추가하여이 문제를 해결할 수 있습니다. 하지만 이 라이브러리를 사용하는 프로그램 / 라이브러리에 생성 된 인스턴스가없는 경우 에만 새 인스턴스를 생성하고 싶습니다 . 인스턴스가 이미 생성 된 경우 . 그러나 인스턴스가 생성 되지 않은 경우 작동하지 않습니다 . 어떤 제안 (전혀 가능합니까?)?logger->log()
logger
logger = new Logger()
Logger
extern Logger logger;
참고 : 이미 Gtkmm4 / Glibmm2.6을 사용하고 있습니다. 아마도 Gtk 또는 Glib를 사용하는 솔루션이있을 수 있습니다.
주석에서 논의했듯이 Singleton 디자인 패턴 을 사용하여 이를 달성 할 수 있습니다 . 그러나이 패턴에는 다음과 같은 몇 가지 단점 이 있습니다.
양질의 소프트웨어를 작성할 때 진정한 문제입니다. 또한 특정 경우에 대해 모든 것이 적절하게 연결되어 있는지 확인하는 방법을 설명하는 이 답변 을 읽어서 싱글 톤의 여러 인스턴스로 끝나지 않도록하십시오.
위에서 언급 한 두 가지 문제를 해결하는 또 다른 방법 인 DI ( 종속성 주입 ) 를 설명하기 위해 여기에 답변을 게시하기로 결정했습니다 . DI를 사용하면 종속성을 생성하지 않고 매개 변수를 통해 주입합니다. 예를 들어 다음 대신 :
Something::Something() {
auto logger = new Logger(); // Dependency creation (not injection)
logger->log("hello !");
}
당신은 다음과 같은 것을 가질 것입니다.
Something::Something(Logger* p_logger) { // Logger dependency injected through p_logger
p_logger->log("hello !");
}
DI는 '하나의 인스턴스'문제를 자체적으로 해결하지 않습니다 . 종속성을 한 번 (일반적으로에서 main
) 생성 한 다음이를 사용하기위한 매개 변수로 전달하도록주의해야합니다. 그러나 글로벌 액세스 문제는 해결되었습니다.
종속성을 추상화하여 다른 수준으로 가져올 수 있습니다. 예를 들어 Logger
클래스에 대한 인터페이스를 작성하고 대신 다음을 사용할 수 있습니다.
// Somewhere in your library:
class ILogger
{
public:
virtual ~ILogger() = default;
virtual void log(const std::string& p_message) = 0;
virtual void set_name(const std::string& p_name) = 0;
};
// In Logger.hpp:
class Logger : public ILogger {
public:
Logger();
void log(const std::string& p_message) override;
void set_name(const std::string& p_name) override;
private:
std::string m_name;
};
// In something.hpp/cpp:
Something::Something(ILogger* p_logger) { // Logger dependency injected through p_logger
p_logger->log("hello !");
}
이를 달성하려면 main
다음과 같이 보일 수 있습니다 .
int main(int argc, char* argv[]) {
// Here, you create your logger dependency:
std::unique_ptr<ILogger> concreteLogger = std::make_unique<Logger>();
concreteLogger->set_name("frederic");
// Here, you inject it. From here on, you will inject it everywhere
// in your code. The using code will have no idea that under the hood,
// you really are using the Logger implementation:
some = new Something(concreteLogger.get());
// Note: if you use `new`, do not forget to use `delete` as well. Otherwise,
// check out std::unique_ptr, like above.
return 0;
}
이것의 장점은 이제 로거에 대해 main
신경 쓰지 않고 언제든지 로거의 구현을 변경할 수 있다는 것입니다. 단위 테스트를 원하는 경우에 대비하여 로거 모의를 만들 수도 있습니다 Something
. 이것은 단위 테스트에서 싱글 톤을 처리하는 것보다 훨씬 더 유연하며, 용어 적으로 모든 종류의 (조사 / 해결하기 어려운) 문제를 생성합니다. 이것은 측면에서 위에서 언급 한 두 번째 문제를 해결합니다.
DI의 가능한 단점은 많은 매개 변수를 갖게 될 수 있다는 것입니다. 그러나 제 생각에는 싱글 톤을 사용하는 것보다 여전히 우수합니다.
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다