
Introduction
Surely the first question that arose when you looked at the headline was “
Shta? ”. In fact, I just translated the phrase "
Inversion of management, dependency injection " into Google Translate into Chinese, and then back. What for? Then, in my opinion, this is a good illustration of what is really happening. People around
confuse ,
distort and
pervert these concepts. On duty, I do a lot of interviews, and 90% of what I hear when I ask a question about DI - frankly, frank nonsense. I did a search on Habra and found several articles that try to cover this topic, but I can’t say that I liked them a lot (ok, ok, I looked through only the first three pages, I confess). Here on Habré I met in the comments such an IoC decoding as
Injection of Container . Someone seriously assumes that there is a mechanism for the injection of containers, which coexists somewhere near the DI, and, apparently, even does something similar. Only with containers. Hmm. In fact, to understand the introduction of dependence is very simple, you just have to ...
Surprisingly, but the fact is that this thing with “just ...” really works! Otherwise, you wouldn't be here, would you?
Richard Feynman was an amazing storyteller who knew how to clearly and easily explain very complex things (see, at least, this
video ).
Joel Spolsky believes that a truly intelligent programmer must necessarily be able to express himself in a human language (and not just in C). Well, probably, almost everyone knows the aphorism of
Albert Einstein : "
If you can’t explain a six-year-old child, you don’t understand it ." Of course, I am not going to compare you with six-year-old children, but nevertheless I will try to tell you about DI, IoC and another DI as simple and clear as possible.
')
Nb . Did you know that in addition to introducing dependencies through the
constructor and
setter , there is also a third way - implementation via the
interface ? Although it is beyond the scope of this article, but I bet that either you have now discovered something new, or at least you have already prepared a rotten tomato.
Inversion of Control (Inversion of Control)
What do you do on your day off? Maybe read books. Maybe playing video games. Maybe you write code, or you can sip a beer while watching the next series (instead of planting Mars with apple trees). But whatever you do, all day is at your complete disposal and only you
manage his schedule.
However, unfortunately, the weekend is over, it is Monday, and you have to go to work (unless, of course, you have it). Under the terms of the employment contract, you must be in place at 8 am. You work until noon. Then you have a break for lunch, and then another four hours of vigorous activity. Finally, at 5:00 pm you get out of the office and go home, where you can relax again and build a pivandria. Feel the difference?
You no
longer manage your daily schedule, it is done by
someone else - your employer.
Consider another example. Suppose you are writing an application with a text interface. In your Main function, you request user input, expect a sequence of characters from a user, call subroutines to process the received data (maybe even in separate streams), and request functions of a general nature from the connected libraries. Thus, all power is concentrated in your hands, and the code you write completely
controls the flow of the application.
But one day the boss enters the office and reports the most unpleasant news - the console is no longer in fashion, the world is ruled by graphical interfaces, which means everything needs to be redone. Being modern and flexible (it’s not just about your yoga classes) being a programmer, you immediately start making changes. To do this, you connect the GUI framework and write event handling code. If this button is pressed, then this and that must be done. And if the user has changed his choice in the drop-down list, then one cannot do without this and that. Everything is going well, but then you realize that it was somehow different before. And who actually calls these event handlers that you program so hard? Who generally determines where and when the user clicked? What is going on? Where is my socks? The GUI framework was obviously more cunning than you thought, and took
control of the flow of the application from you.
This is the
Inversion of Control - a very abstract principle, postulating the fact of setting the flow of execution to some external entity with respect to you.
The concept of IoC is closely related to the concept of the
framework . This is the main characteristic that distinguishes it from another way of formatting reusable code - a
library , the functions of which you simply call from your program. The framework is an external framework providing pre-defined extension points. You insert your code into these extension points, but when it is called, it is the framework that determines.
As a homework, ponder why
Jeff Sutherland insists that SCRUM is a framework, not a methodology.
Dependency Inversion
This is the same letter D in the abbreviation
SOLID - the principle that says that:
- modules of the upper levels should not depend on the modules of the lower levels. Both types of modules must depend on abstractions;
- abstractions should not depend on details. Details must depend on abstractions.
A bit confusing wording, so consider the following example (for examples I'll use C #).
public class Foo { private Bar itsBar; public Foo() { itsBar = new Bar(); } }
The problem here is that the class Foo depends on the specific class Bar. For one reason or another - for the sake of extensibility, reusability, or testability for the sake of it - there may be a task to separate them. According to the principle of dependence inversion, for this purpose, an intermediate abstraction should be introduced between them.
public class Foo { private IBar itsBar; public Foo() { itsBar = new Bar(); } }
The UML diagram demonstrates both options.
Difficulties begin when you ask, and where is the inversion here? The basic idea, without understanding of which it is impossible to answer this question, is that the
interfaces belong not to their implementations, but to the clients using them . The name of the interface IBar is misleading and makes you consider the IBar + Bar combination as a whole. At the same time, the true owner of IBar is the Foo class, and if you take this into account, the direction of the connection between Foo and Bar will really be reversed.
Dependency Injection
Looking at the resulting code, the attentive reader will, of course, notice that even despite the introduction of an intermediate abstraction, the Foo class is still responsible for instantiating the Bar class. Obviously, this is not exactly the division that could be expected.
public class Foo { private IServer itsServer; public Foo() { itsServer = new Bar(); } }
To rid the Foo class of such an unpleasant duty, it would be good to move the code of instantiation somewhere else and encapsulate it there (since we are all extremely pragmatic and do not like to write anything twice). This can be done in two ways - using either Service Locator or Dependency Injection.
Service Locator is such a register of correspondence of abstractions and their implementations. You feed him the interface you are interested in, and in return you get a ready-made copy of a particular class. It looks like this:
public class Foo { private IServer itsServer; public Foo() { itsServer = ServiceLocator.Resolve<IServer>(); } }
The caveat is that the class Foo is now completely independent of the class Bar, but still
controls its instantiation. As we already know, this can be avoided by
inverting the control flow , i.e.
transferring this control into the hands of some
external mechanism . Dependency Injection is such a mechanism implemented in the form of frameworks called IoC containers:
public class Foo { private IServer itsServer; public Foo(IServer server) { itsServer = server; } }
Conclusion
In fact, an IoC container is such a stupid name that it is even difficult to think of something worse. It says absolutely nothing about what it actually does, misleading dozens of new and new programmers every day. Absolutely any framework can be called an IoC container, since by definition it implements control inversion and is a wrapper for some general-purpose code. This term was (and continues to be) so disgusting that
Martin Fowler came up with another - the introduction of dependence.
To summarize We use
Dependency Inversion to separate modules by abstraction,
Dependency Injection , in order to get rid of instintiation manually, and we implement it through a framework built on the principle of
Inversion of Control . And none of this is synonymous, so
IoC containers are a vivid example of how to confuse everyone with the help of one single unsuccessful term.
I hope in this little opus I managed to convey to you the difference between these things, and you will never confuse them again. And if one of your colleagues confuses you, you can simply and clearly explain to them what they are wrong about.