While trying to solve an issue, explained on the StackOverflow forum, somebody advised me to use dependency injection. For personal reasons, the moment a person mentions to me the usage of a design pattern, I always start thinking of very difficult constructions. After some thinking and investigating, I've invented following construction on my own (pseudo-code):
General header file
interface ILogger {
public:
void writeMsg(std::string);
};
class Application_Logger : ILogger {
public:
void writeMsg(std::string output) {
std::printf(stringutils::format("Application : %s", output);
}
};
class Test_Logger : ILogger {
public:
void writeMsg(std::string output) {
std::printf(stringutils::format("Test : %s", output);
}
};
Application.h header file:
ILogger logger = nullptr;
Unit_test.h header file:
ILogger logger = nullptr;
Application_startup.cpp:
if (logger == nullptr)
ILogger logger = Application_Logger();
Unit_testing_startup.cpp:
if (logger == nullptr)
ILogger logger = Test_Logger();
Common_used.cpp:
logger.writeMsg("<information>");
logger.writeMsg("<more information>");
Application output
Application : <information>
Application : <more information>
Unit test output
Test : <information>
Test : <more information>
I have no idea whether or not this works, but I believe it does (assuming that it is possible to launch a piece of code, enabling to fill in the interface pointer).
In my opinion, this is not a special construction, but the basic usage of interfaces, not worthy of being called a design pattern. Am I correct and in case not, what needs to be added/modified in order to make a direct injection pattern out of this?
After some first comments, I'm starting to understand why this is not a direct injection, so hereby another example, trying to make a very simple constructor direct injection (just being able to switch between two ILogger interfaces):
General header file
interface ILogger {
public:
void writeMsg(std::string;int);
};
class Simple_Logger : ILogger {
public:
void writeMsg(std::string output;int severity) {
std::printf(stringutils::format("[%d] : %s", severity, output));
}
};
class Detailed_Logger : ILogger {
public:
void writeMsg(std::string output;int severity) {
if (severity == 0) {
std::printf(stringutils::format("Very important : %s", output));
} else {
std::printf(stringutils::format("[%d] : %s", severity, output));
}
}
};
Application.h header file:
ILogger logger = nullptr;
Application_startup.cpp (based on args
):
if (logger == nullptr) {
if args == "Simple"
ILogger logger = Simple_Logger();
else : ILogger logger = Detailed_Logger();
}
If this is correct, it means that DI means that your application's processing is based on interfaces, and that you let outer data (arguments, configuration file content, interactive input, …) decide which implementation is chosen for those interfaces. Am I right this time? (On the Wikipedia page, it's not clear where the mentioned service
is coming from)
Best Answer
I agree with the other answers, but it appears there is still some confusion. So let's look at an example.
It's important that
DataProcessor
does not create or rely on any specificLogger
implementation, but just on the interface. This allows you to inject any concrete implementation ofLogger
during runtime.Note that I've shown constructor injection. There are other ways to inject depdencies, but the general idea is that classes which the
DataProcessor
uses (i.e. its dependencies) are not created by theDataProcessor
but injected into it from the outside.