While learning about SOLID principles, specifically Dependency Inversion (D of SOLID), we come across references and confusions surrounding Inversion of control. Reading about the two did no give me straightforward answers. On top of it, looking at the sample code for the patterns referred to, for implementing Inversion of Control did not seem like inverting any control. Hence, I started checking stack overflow, blogs, et al. Writing this blog to share my understanding and collation of that information.
Before getting into details, the statements from Martin Fowler's article which gave me the right direction on differentiation on these three:
DI is about wiring,
IoC is about direction,
and DIP is about shape.
This gave a good perspective of how to look at definition and details for each of these.
Lets try to break down couple of these terms -
Dependency:
Being dependent means to require someone or something to support. Dependency in object oriented world is a relationship between objects, where, one object is dependent on other object(s) for its implementation. Think about hierarchy, inheritance, composition, etc.
Control:
Being in control means, to be able to determine the behavior or supervise the running of or maintain influence or authority over. So here we are talking about how functionality is executed or which portion of the application is taking control to execute a particular function.
Inversion:
Dictionary definition of inverted is - put upside down or in the opposite position, order, or arrangement. So in this case as well we are talking about inversion in terms of something being inverted or opposite direction. "What", we will come to in a minute.
Dependency Inversion:
The principle states:
High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces).
Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.
Taking the above definition and dictionary references together and putting simply, we get - the direction of which object depends on which objects is inverted.
While thinking about Dependency Inversion - Think about shape
of the object.
Before DIP -
You will find loads of examples online for implementing DIP. Not getting into too much details, below diagram represents before DIP state of the classes. Where high level module depends directly on low level module. Think about high level module using the function of low level module to execute its high level function successfully.
With DIP -
However, with DIP, high level module does not directly depend on the low level module. It is depending on the interface (object of the same will be provided to high level module at run time). In fact both are dependent on the interface. Thus, if we need to replace the low level module with a new one, that can be done without any impact to high level module. We can even say that High level module would not know the difference.
Inversion of Control:
Inversion of control reflects the model of relationship between a caller and callee. In classic flow of control the client has full control on environment and sequence of calls to library methods. But in case of IoC, callee takes control over some calls between callee and caller e.g. call-backs. Think about direction
in flow of control. This is a common experience while extending functionality of a framework.
Quoting from Martin Fowler's blog -
The control is inverted - it calls me rather me calling the framework. This phenomenon is Inversion of Control (also known as the Hollywood Principle - "Don't call us, we'll call you").
Dependency Injection:
According to Wikipedia,dependency injection is a technique in which an object receives other objects that it depends on, called dependencies. Instead of the client specifying which service it will use, the injector tells the client what service to use.
Thus, Dependency injection is a way to implement IoC. Where the control to create the object is not with the class itself. Think about wiring
up of the objects to the classes which need to use them.
Some examples of IoC:
Below are some techniques of implementing IoC. However, there were some gotchas for me which I've described here. For details of what / how of these techniques, please check those individual topics else where.
Dependency Injection: Dependency is injected at run time by DI container.The class receiving the object has not control on which actual object is created or how.
Strategy Pattern: In this pattern, the strategy / functionality / behavior can be swapped out at runtime.
As against most examples we find for implementation of strategy pattern, if we are looking at this pattern from IoC point of view then the responsibility of selection of strategy / function / behavior should be done by a different class. The client does not need to know which strategy to select. If strategy selection is done by client then it is not IoC.
Service Locator: In service locator the class requests for the object (or service) by name. If object does not exist then it would create the object and return (like factory pattern). However, if it exists then it will just return that object from cache (like registry pattern).
Template Method: It defines skeletal operations in terms of high level steps. Actual implementation of those steps is done by sub class.
Example: A abstract class called Game
with below abstract
methods -
Start()
Score()
End()
And a play method as -
function Play()
{
Start();
Score();
End();
}
Now, sub-classes like Cricket
or Soccer
will implement Start()
, Score()
and End()
functions. When client creates object of sub-class and executes play method the Start()
, Score()
and End()
functions get called in sequence. The sequence of these calls are in turn inverted control as Cricket
or Soccer
classes have no control on when they get called.
Summary:
With all the people trying to simplify the understanding of Inversion of Control and Dependency Inversion Principle the only thing giving me clear direction was these words from Martin Fowler - DI is about wiring
, IoC is about direction
, and DIP is about shape
. Additionally, patterns referred to which help in implementing IoC need to be implemented in specific way else they are true to the pattern but are not implementing IoC. Hope this article sheds some light on those items as well.
References:
https://martinfowler.com/articles/dipInTheWild.html
https://martinfowler.com/bliki/InversionOfControl.html
https://www.martinfowler.com/articles/injection.html
https://en.wikipedia.org/wiki/Inversion_of_control
https://en.wikipedia.org/wiki/Dependency_injection
Top comments (0)