What would be an addiction and an injection?
I think that’s the first question to support the theme.
Dependency is any code (class, library, method, etc.) that another code needs to do use task, or fulfill its purpose.
Imagine that a code needs to sum two values and return that value. Let’s put this in a method (I’ll use C#
in the examples):
int Somar(int num1, int num2)
{
return num1 + num2;
}
This code can do everything needed using the sum operator +
, only using two basic type parameters, int
.
Now let’s imagine that to do this the method takes two decimal numbers, but wants to return a rounded value in 2 houses. To use a class that already does that:
double Somar(double num1, double num2)
{
AlgumaClasse objetoQueCalcula = new AlgumaClasse();
return objetoQueCalcula.Arredondar(num1 + num2, 2);
}
In this case, I used an "external" code and this small piece of code, AlgumaClasse
, and created a dependency with that other code. You see, this very code knows this dependency, and it itself has solved how to "get" an instance of this class, creating its own object by doing AlgumaClasse objetoQueCalcula = new AlgumaClasse();
Injection would be an implementation where, in place of the method Somar
know and himself build the dependency that needs, he receive this implementation already ready, so:
double Somar(double num1, double num2, AlgumaClasse objetoQueCalcula)
{
return objetoQueCalcula.Arredondar(num1 + num2, 2);
}
Now the method only needs to worry about what it needs to do, leaving in charge that whoever is to use, provide everything it needs.
Here is just a basic example, if we pass the class type we will fall into another affection problem that we will see later.
What would be the main mechanism behind dependency injection?
The purpose of the mechanism is to leave a code independent of the creation of its dependencies. Here at Sopt we have this related question: What is addiction injection?.
Taking advantage of the good answers, here are some definitions:
Dependency Injection is a type of Control Inversion and means
that a class is no longer responsible for creating or fetching objects
on which depends.
And
It’s a Design Pattern that nails a kind of external control, a
container, a class, file settings, etc., insert a
dependency on another class.
That is, the control over the creation of dependencies is passed to another code responsible for this, a code that knows how to create/provide the instances that other codes will need. In the other question was used the term container, that has nothing to do with containers of Docker. This container know the dependencies that are needed and create/inject them into the codes. Let’s use this name, without worrying exactly about the concept of container.
Here is an important point:
Interfaces are contracts that classes must implement, i.e., describes the members (methods, properties) that a class must have. What do interfaces have to do with injection? Instead of controlling the creation of dependencies, we solve some problems of involvement and instances (we’ll talk later), but when passing the class, we create another problem of involvement: if I need to change the class itself, use another one instead, change its name, etc, it will also be necessary to change the code that depends on that class, which is a problem.
In the above example, if the class AlgumaClasse
If you changed your name, or needed to use some other similar class to replace it (let’s talk about it later), you would need to change all the methods that use that class, and if you have to change all the other codes that use that dependency, then everyone must change. Now let’s look at this implementation with interfaces
:
interface IAlgumaCoisa
{
double Arredondar(double num1, double num);
}
Here we create a contract saying that "Whoever uses Ialgumacoisa will have a Round method, which returns a double and gets two double numbers". This is exactly what our little code needs, changing the implementation will be:
double Somar(double num1, double num2, IAlgumaClasse objetoQueCalcula)
{
return objetoQueCalcula.Arredondar(num1 + num2, 2);
}
The code stays the same, but now it depends even less on the class itself, because it only knows the contract. We could have:
public class AlgumaClasse: IAlgumaClasse
{
public double Arredondar(double num1, double num)
{
... faz alguma coisa ..
}
}
And also
public class AlgumaOutraClasseQualquer: IAlgumaClasse
{
public double Arredondar(double num1, double num)
{
... faz alguma coisa ..
}
}
We could use either of the two classes, because for the method Somar
, as long as the concept is respected, it matters little.
Now back to the mechanism itself.
Thus the container, or any other name you want to give yourself, you can register these dependencies and inject it into the code itself, something like that for example:
Container.RegistrarDependencia(IAlgumaClasse, AlgumaOutraClasseQualquer);
I mean, "when someone needs Gumialaclasse, use Someone else.
To do this, there are several libraries, packages, etc that already do this. In .Net
we have the IServiceProvider
and other packages like SimpleInjector
, Ninject
and Unity
, all of them implement the injection mechanism, what needs to be done is, record the interfaces and what class, or how to build an object that implements that interface. These dependency injection engines work with interfaces, so I broached the subject before.
The above example makes use of dependency injection or dependency injection only possible with object orientation?
The question code example is:
def funcao_tarefa(d):
dpendencia = inject.instance(d)
dpendencia()
funcao_tarefa('dum')
funcao_tarefa('ddois')
funcao_tarefa('dtest')
That is, the method task receives a "d" parameter and uses an external code (container, Provider, whatever the definition) that, from a previous record of the dependencies, provides the dependency that the method needs, so this can be considered as dependency injection.
Dependency injection serves to decrease code coupling and make code more testable?
Yes, that’s the goal. By removing the involvement, we leave the code independent of the creation of its dependencies. As stated earlier, it causes changes in dependencies, so much so that respected contracts, will not necessarily need changing in the code that uses dependencies.
But we have one more point much important: testing.
Highly coupled code is difficult to test. Imagine a code for example that uses a database to retrieve data and perform some task:
public bool AutenticarUsuario(string usuario, string senha)
{
BancoDeDados bd = new BancoDeDados();
Usuario usr = bd.ObterUsuario(usuario);
return usr != null && usr.Senha == senha;
}
When writing a unit test, which should test only this basic unit, ie "given a user/password, get the user of a database and validate if it is and the password is the same informed", how to do without using a real database? It would not be possible.
Now if that code were like that:
public bool AutenticarUsuario(string usuario, string senha, IBancoDeDados bd)
{
Usuario usr = bd.ObterUsuario(usuario);
return usr != null && usr.Senha == senha;
}
Thus, I could, as in the previous example, create an IBA implementation, other than a database, to control what is returned (null
, user with correct password or not) and test the behavior of this method, technique that is commonly known as Mock
, or create an object that has the controlled behavior for the test, and then register that "Mock" in the dependency controller:
Container.Registrar(IBancoDeDados, MinhaClasseMock);
Or, in a slightly more real example:
Container.Add<IBancoDeDados, Mock<IBancoDeDados>>();
That is, dependency injection decreases the coupling, which allows in addition to making the code independent of the creation of its dependencies, more easily "testable".
And what would be the cons of addiction injection?
As we saw in the code examples, we need to implement more code:
- Record all dependencies (some may not be as simple, and depend on others or specific conditions, which makes it difficult to register);
- To make the most of it, we must separate the contracts from the implementation of the classes, which results in more code;
- To not have to do "everything at hand", you need to use libraries/packages/frameworks that manage dependency injection.
But by comparing it to the benefits, it seems, at least in my opinion, to be much more advantageous.
To demonstrate this, I created a little code, but unfortunately the dotnetfiddle
error when using the Moq
, and the ideone
does not accept Packages :(
But whoever wants to see or test site is here: https://dotnetfiddle.net/Sg2VMW
Related (or partial dup): https://answall.com/q/21319/101, https://answall.com/q/195174/101, https://answall.com/q/396507/101 and https://answall.com/q/86484/101
– Maniero