Definition: High-level modules should not depend on low-level modules, both should depend on their abstractions; abstractions should not depend on details; details should depend on abstractions.
The origin of the problem: Class A directly depends on class B. If you want to change class A to depend on class C, you must modify the code of class A to achieve it. In this scenario, class A is generally a high-level module responsible for complex business logic; class B and class C are low-level modules responsible for basic atomic operations; if class A is modified, it will bring unnecessary risks to the program.
Solution: Modify class A to depend on interface I, class B and class C implement interface I respectively, and class A indirectly contacts class B or class C through interface I, which will greatly reduce the probability of modifying class A.
The Dependency Inversion Principle is based on the fact that abstract things are much more stable than details are variable. Architectures built on abstraction are much more stable than architectures built on details. In Java , abstract refers to an interface or abstract class, and details are specific implementation classes. The purpose of using interfaces or abstract classes is to formulate specifications and contracts, without involving any specific operations, and leave the task of showing details to their implementation class to complete.
The core idea of the Dependency Inversion Principle is interface-oriented programming, and we still use an example to illustrate how interface-oriented programming is better than implementation-oriented programming. The scene is like this, the mother tells the story to the child, as long as she gives her a book, she can tell the story to the child according to the book. code show as below:
- class Book{
- public String getContent(){
- return "A long, long time ago there was an Arabic story..." ;
- }
- }
- class Mother{
- publicvoid narrate(Book book){
- System.out.println( "Mom started telling a story" );
- System.out.println(book.getContent());
- }
- }
- public class Client{
- publicstaticvoid main(String[] args){
- Mother mother = new Mother();
- mother.narrate(new Book());
- }
- }
operation result:
Mom started telling stories
long, long ago there was an Arabian story...
It works well, if one day, the demand becomes this: not a book but a newspaper, let the mother tell the story in the newspaper, the code of the newspaper is as follows:
- class Newspaper{
- public String getContent(){
- return "Lin Jeremy Lin led the Knicks to beat the Lakers 38+7..." ;
- }
- }
这位母亲却办不到,因为她居然不会读报纸上的故事,这太荒唐了,只是将书换成报纸,居然必须要修改Mother才能读。假如以后需求换成杂志呢?换成网页呢?还要不断地修改Mother,这显然不是好的设计。原因就是Mother与Book之间的耦合性太高了,必须降低他们之间的耦合度才行。
我们引入一个抽象的接口IReader。读物,只要是带字的都属于读物:
- interface IReader{
- public String getContent();
- }
Mother类与接口IReader发生依赖关系,而Book和Newspaper都属于读物的范畴,他们各自都去实现IReader接口,这样就符合依赖倒置原则了,代码修改为:
- class Newspaper implements IReader {
- public String getContent(){
- return "林书豪17+9助尼克斯击败老鹰……";
- }
- }
- class Book implements IReader{
- public String getContent(){
- return "很久很久以前有一个阿拉伯的故事……";
- }
- }
- class Mother{
- public void narrate(IReader reader){
- System.out.println("妈妈开始讲故事");
- System.out.println(reader.getContent());
- }
- }
- public class Client{
- public static void main(String[] args){
- Mother mother = new Mother();
- mother.narrate(new Book());
- mother.narrate(new Newspaper());
- }
- }
运行结果:
妈妈开始讲故事
很久很久以前有一个阿拉伯的故事……
妈妈开始讲故事
林书豪17+9助尼克斯击败老鹰……
这样修改后,无论以后怎样扩展Client类,都不需要再修改Mother类了。这只是一个简单的例子,实际情况中,代表高层模块的Mother类将负责完成主要的业务逻辑,一旦需要对它进行修改,引入错误的风险极大。所以遵循依赖倒置原则可以降低类之间的耦合性,提高系统的稳定性,降低修改程序造成的风险。
The use of the dependency inversion principle brings great convenience to the parallel development of multiple people. For example, in the above example, when the Mother class is directly coupled with the Book class, the Mother class must wait for the Book class to be encoded before coding, because the Mother class depends on in the Book class. The modified program can be started at the same time without affecting each other, because Mother has nothing to do with the Book class. The more people involved in collaborative development and the larger the project, the more important it is to adopt the dependency-induced principle. The now popular TDD development model is the most successful application of the Dependency Inversion Principle.
There are three ways to transfer dependencies. The method used in the above example is interface transfer. There are also two transfer methods: constructor transfer and setter method transfer. I believe that those who have used the spring framework will not be unfamiliar with the transfer method of dependencies. .
In actual programming, we generally need to do the following three points:
- Low-level modules should try to have abstract classes or interfaces, or both.
- Variables should be declared as abstract classes or interfaces as much as possible.
- Follow the Liskov substitution principle when using inheritance.
The core of the Dependency Inversion Principle is to ask us to interface-oriented programming , and to understand interface-oriented programming, we also understand Dependency Inversion.