This principle corresponds to ‘D’ in the SOLID abbreviation.
This principle says that high-level modules should not depend on low-level modules (any class which is dependent/using any other classes inside it, is called a high-level class. The used classes are called low-level classes).
They should only depend on abstractions.
In another saying, never depend on concretes, depend only on abstractions.
For instance, in three-layered architecture;
- The Presentation Layer depends on Business Layer.
- Business Layer depends on Data AccessLayer.
In the sample below; GUI’s buttonClick method depends on the business layer’s save method, and the business layer’s save method depends on the data access layer’s save method.
Sample
1st Iteration
The problem here is; classes are tightly coupled.
So, for instance, when another DataAccessLayer Class is needed to be used
then BusinessLayer Class is also needed to be updated.
This is a very simple example but please consider a large code base, high complexity, and many dependencies.
package firstIteration;
public class DataAccessLayer {
public void save() {
...
}
}
package firstIteration;
public class BusinessLayer {
DataAccessLayer dataAccessLayer = new DataAccessLayer();
public void save() {
...
dataAccessLayer.save();
}
}
package firstIteration;
public class GUI {
private BusinessLayer businessLayer = new BusinessLayer();
public void buttonClick() {
...
businessLayer.save();
}
}
// Application Class
// Sample's 1st Iteration
package firstIteration;
public class Application {
public static void main(String[] args) {
GUI a = new GUI();
a.buttonClick();
}
}
// GUI -> BusinessLayer -> DataAccessLayer
// buttonClick() -> save() -> save()
2nd Iteration
The solution to the problem mentioned in the previous section is constructing dependencies via abstract entities (such as interfaces) as this principle tells.
This is Dependency Injection.
package secondIteration;
public interface DataAccessLayerInterface {
public void save();
}
package secondIteration;
public class DataAccessLayer implements DataAccessLayerInterface {
public void save() {
...
}
}
package secondIteration;
public class DataAccessLayer2 implements DataAccessLayerInterface {
public void save() {
...
}
}
package secondIteration;
public interface BusinessLayerInterface {
public void save();
}
package secondIteration;
public class BusinessLayer implements BusinessLayerInterface {
DataAccessLayerInterface dataAccessLayer;
public BusinessLayer(DataAccessLayerInterface dataAccessLayer) {
super();
this.dataAccessLayer = dataAccessLayer;
}
public void save() {
...
dataAccessLayer.save();
}
}
package secondIteration;
public class BusinessLayer2 implements BusinessLayerInterface {
DataAccessLayerInterface dataAccessLayer;
public BusinessLayer2(DataAccessLayerInterface dataAccessLayer) {
super();
this.dataAccessLayer = dataAccessLayer;
}
public void save() {
...
dataAccessLayer.save();
}
}
package secondIteration;
public class GUI {
private BusinessLayerInterface businessLayer;
public GUI(BusinessLayerInterface businessLayer) {
super();
this.businessLayer = businessLayer;
}
public void buttonClick() {
...
businessLayer.save();
}
}
package secondIteration;
public class Application {
public static void main(String[] args) {
GUI a = new GUI(new BusinessLayer(new DataAccessLayer()));
a.buttonClick();
System.out.println("----------");
// no need to code change in that case,
// only send new DataAccessLayer2 and BusinessLayer2
// concrete classes as input parameters
GUI a2 = new GUI(new BusinessLayer2(new DataAccessLayer2()));
a2.buttonClick();
}
}
// GUI -> BusinessLayer -> DataAccessLayer
// GUI -> BusinessLayer2 -> DataAccessLayer2
// buttonClick() -> save() -> save()
This principle is only a guideline. Dependency Injection is an implementation of this principle/guideline.
This is the end of the series. Please also check my other articles, here.
Thanks for reading.