Sometimes it seems like we are talking about the same thing (of course, it is not) when these concepts are being used. What is the real difference between them? When to use one or the other?
Sometimes it seems like we are talking about the same thing (of course, it is not) when these concepts are being used. What is the real difference between them? When to use one or the other?
Inversion of Control (or IoC, Inversion of Control) is a broader term to refer to a behavior (explained below). There are a number of ways to implement control inversion.
Dependency injection is no longer so broad, it's actually considered up to a design pattern.
Dependency injection is one way to do Control Inversion. So when you use a or other it does not make much sense.
But the confusion is very common, it is not uncommon to find the terms being used interchangeably.
I believe that explaining one and then the other demystifies this confusion.
Control inversion is a present behavior when we use some frameworks. Basically the idea is to separate * what * from * when *.
Here are two examples (abstract code), the usual form and the one with IoC.
Usual form
perguntarNome()
lerEntrada()
perguntarIdade()
lerEntrada()
SE verificarSeDadosEstaoValidos()
ENTAO cadastrar()
SENAO exibirMensagemDeErro()
With IoC
quandoPerguntarNome(lerEntrada)
quandoPerguntarIdade(lerEntrada)
paraVerificarValidade(verificarSeDadosEstaoValidos)
paraCadastrar(cadastrar)
quandoHouverErro(exibirMensagemDeErro)
In the second example we unravel what will be done with the moment it will be done; we separate the implementations from the stream. This way it is easier to reuse or change only part of the code (without having to modify / understand * the whole *).
Notice that there is a loophole to fit various programming concepts, such as object-oriented principles, event-driven design, and more.
I will stop here; this is in essence what we call Control Inversion .
Dependency injection is one way to perform Control Inversion.
The technique is to pass the dependency (the service) to the dependent (the client). This is called injection. The important thing is to understand that we inject the service into the client, rather than the client itself looking for and constructing the service it will use.
I think there is nothing better than a practical example to understand this concept. Here's a real (simplified) example I've already implemented on one of the systems I've worked on (C # code):
public interface ILogger
{
void Logar(string mensagem);
}
public class LogEmArquivo : ILogger
{
void Logar(string mensagem)
{
// Loga a mensagem em um arquivo de log
}
}
public class LogPorEmail : ILogger
{
void Logar(string mensagem)
{
// Envia a mensagem por email aos responsáveis
}
}
public class Principal
{
private ILogger _logger;
public Principal(ILogger logger)
{
_logger = logger;
}
// Chamado pelos métodos internos da classe Principal
// quando há necessidade de logar alguma informação
private void LogarInformacao(string informacao)
{
_logger.Logar(informacao);
}
}
The Principal
class needs a ILogger
to work, but it does not "see" or construct the implementation it will use, this will be done by who will build the Principal
class. This way the log action implementation is decoupled with whatever the main class will do.
As I said, this is a real example that I came to use. In my case, the application had a directive in the configuration file indicating which environment it was running in (Development, Testing, Homologation, Production ...). At startup, the application checked which environment and, depending on this configuration, uses a different logger for each environment. Basically in Development and Tests we used only one log in file, while in Homologation and Production we used email sending where the problems were more serious).
At runtime, the application programmatically changed its logger implementation to be used. This is one of the advantages of having the code decoupled.
One of the forerunners to discuss the subject is the author Martin Fowler. For those who want to go deeper into the topic, you should read the articles on the subject on your blog:
IoC may not be as well created in common projects, however it is widely used in most applications. IoC is also the Hollywood Principle , where producers tell actors "do not call us, we'll call you." This means that a fraework component takes control of the application and when it needs your intervention it calls what you will have to provide . The most common case is a event loop present in almost all mechanisms of games and window managers on the market. Some ERPs also work like this, they take care of the whole process and call the parties that the "users" can customize the operation.
Framework is important here, since IoC is the basis of > frameworks .
Dependency injection is just one way to apply IoC. I have seen many formal definitions but the concept is extremely simple. You are doing DI when you remove the dependencies that exist in a part of the program, possibly a class. It facilitates decoupling.
The best definition of DI is to allow states and behaviors to be determined by passing parameters . That is, is to allow you to refer to a class not yet known during the development of the current class. You eliminate the dependency of a specific class. This can be done through parameters in constructors, common methods, or properties of the current class.
Let us agree that this is a huge facility. It serves to relax the application , even to test it since the test usually needs to be done with simulated assumptions. It is thus possible to create plugins systems, configurations, on-the-fly execution changes, allow for clear separation of responsibilities, clearing code and facilitating development and maintenance, and allows decoupling of the classes.
But there are those who say that full decoupling can bring another problem. You leave the responsibility to the user. I am one of these people who prefer pragmatism than academicism. Of course you may have some case that this total decoupling is really important but for the most part you just want the flexibility. So my stance is to create the parameter yes, but also to provide a default dependency. So the consumer of this class (or function, or component) does not have to worry about creating and passing anything as a parameter when you want to do exactly what that part of the application knows is appropriate and probably desired.
The only "disadvantage" of this is that it creates a dependency for the default implementation. But why is this bad? I think if it was bad and there would be frameworks . Flexibility is advantage, total independence is cheap theory. Will you provide an implementation for a string as a parameter to everything that needs a string ? Everything has a limit. The difference between medicine and poison is the dosage.
Obviously, separating responsibilities also means spreading the parts through the application, which hampers their understanding and eventually maintenance. Readability also has to do with organization. Having the parts scattered makes reading harder. Especially because you spend having many types just to meet the injection and requires much greater planning than is being done (which some will consider advantage and others disadvantage).
I'll give you an example of what talles has already done in his response:
public interface ILogger {
void Logar(string mensagem);
}
public class LogEmArquivo : ILogger {
void Logar(string mensagem) {
//Loga a mensagem em um arquivo de log
}
}
public class LogPorEmail : ILogger {
void Logar(string mensagem) {
//Envia a mensagem por email aos responsáveis
}
}
public class Principal {
private ILogger _logger;
public Principal(ILogger logger) {
_logger = logger;
}
public Principal() {
_logger = new LogPorArquivo();
}
//Chamando métodos privados da classe Principal quando precisa logar alguma informação
private void LogarInformacao(string informacao) {
_logger.Logar(informacao);
}
}
public static Aplicacao {
public static void Main() {
var logArq = new Principal(); //exemplo de uso
var logMail = new Principal(new LogPorEmail()); //exemplo injetando a dependência
}
}
I've placed it on GitHub for future reference.
The use of these resources should serve the application and not the external interests such as the need for testing, for example. When you need to test, use a test pattern suitable for testing . Testing can be done with good tools, not with the way code is created. Organizing the code in one way just because it facilitates testing is something wrong, IMHO. Doing so makes maintenance easy and by the way makes testing easy is ok. If the testing tool you use requires specific coding, switch tools.
Remembering that we should always program to the interface and not to the implementation .