Before delving deeper into the principle we should understand
a. What is a dependency ?
b. What is it trying to invert ? And to understand this we should understand what happens in the normal case which the principle says should be inverted ?
What is dependency ?
When a class refers another class, it is said to be dependent on the other class. Consider the following example,
Class A{
}
class B{
A obj;
}
The class B has a member variable of class A and so it is dependent on A. The dependency can be set either using constructor or setter methods.
What is the 'un-inverted' way ?
An application has high level classes and low level classes. The low level classes would implement the basic functionality and the high level classes would implement complex logic and in turn use the low level classes. A natural way of implementing such structures would be to write low level classes and once we have them to write the complex high level classes. Since the high level classes are defined in terms of others this seems the logical way to do it.
High Level Module --> Low Level Module
Besides, as mentioned by Martin Folwer in his article he has used the word inversion because...
more traditional software development methods, such as Structured Analysis and Design, tend to create software structures in which high level modules depend upon low level modules, and in which abstractions depend upon details. Indeed one of the goals of these methods is to define the subprogram hierarchy that describes how the high level modules make calls to the low level modules. Thus, the dependency structure of a well designed object oriented program is “inverted” with respect to the dependency structure that normally results from traditional procedural methods.
What is DIP ?
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend on details. Details should depend on abstractions.
According to this principle the way of designing a class structure is to start from high level modules to the low level modules:
High Level Classes --> Abstraction Layer --> Low Level
But why ?
2. Its the high level modules which have business logic. Yet,when these modules depend upon the lower level modules, then changes to the lower level modules can have direct effects upon them; and can force them to change. But it should be high level modules that ought to be forcing the low level modules to change.
Advantages of DIP
1. The use of DIP makes the high level modules reusable as they are not directly dependent on low level details/modules. It helps in creation of reusable frameworks.
2. It helps in creation of code that is resilient to change. And, since the abstractions and details are all isolated from each other, the code is much easier to maintain.
Layering and DIPAccording to Booch, “...all well structured object-oriented architectures have clearly-defined layers, with each layer providing some coherent set of services though a well-defined and controlled interface.”
For example, lets consider the three layers A->B->C. In this case the layer A is sensitive to all the changes down in layer C. Dependency is transitive.
Using DIP, A->IB->BImpl->IC->CImpl
Here, each of the lower layers are represented by an abstract class. Each of the higher level classes uses the next lowest layer through the abstract interface. Thus, none of the layers
depends upon any of the other layers. Instead, the layers depend upon abstract classes. Not only is the transitive dependency of A Layer upon C Layer broken, but even the direct dependency of A Layer upon B Layer broken.
The point is to exploit polymorphism by programming to a supertype so that the actual runtime object is not locked into the code. And the supertype can be an interface or an abstract class.
Guidelines (Not rules !!!)
1. No variable should hold a reference to a concrete class. (If you use new, you would hold reference to a concrete class. Use factory to get that around.)
2. No class should derive from a concrete class.
3. No method should override an implementation method of any of its base classes.