03.依赖倒置原则 (DIP)

DIP全称

DIP, Dependence Inversion Principle , 依赖倒置原则

定义

模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口和抽象类产生的。

面向接口编程,或者说是面向抽象编程。依赖抽象(接口或者抽象类),而不依赖具体实现。高层次(调用端)的模块不依赖于低层次(实现类)的模块的实现细节。

优点

  1. 降低类之间的耦合性
  2. 提高系统的稳定性
  3. 降低修改程序造成的风险

实现

  • 问题由来: 类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。

  • 解决方案:
    将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。
    以抽象方式耦合是依赖倒转原则的关键。抽象耦合关系总要涉及具体类从抽象类继承,并且需要保证在任何引用到基类的地方都可以改换成其子类,因此,里氏代换原则是依赖倒转原则的基础。

实例

场景:母亲给孩子讲故事,只要给她一本书,她就可以照着书给孩子讲故事了。

class Book {
    public String getContent() {
        return "很久很久以前......";
    }
}

class Monther {
    public void narrate(Book book) {
        System.out.println("妈妈开始讲故事");
        System.out.println(book.getContent());
    }
}

public class DIPClient {
    public static void main(String[] args) {
        Monther monther = new Monther();
        monther.narrate(new Book());
    }
}

输出结果:
妈妈开始讲故事
很久很久以前......

如果此时需要讲报纸上的内容,就需要再新建一个类Newspaper

class Newspaper {
    public String getContent() {
        return "金融风暴卷土而来......";
    }
}

class Book {
    public String getContent() {
        return "很久很久以前......";
    }
}

class Monther {
    public void narrate(Book book) {
        System.out.println("妈妈开始讲书上的故事");
        System.out.println(book.getContent());
    }

    public void narrate(Newspaper newspaper) {
        System.out.println("妈妈开始讲报纸上的内容");
        System.out.println(newspaper.getContent());
    }
}

public class DIPClient {
    public static void main(String[] args) {
        Monther monther = new Monther();
        monther.narrate(new Book());

        monther.narrate(new Newspaper());
    }
}

输出结果:
妈妈开始讲书上的故事
很久很久以前......
妈妈开始讲报纸上的内容
金融风暴卷土而来......

如果再来个讲头条的内容,又要新建一个类,然后改动Monther类,这样做不太合理。此时新建接口类,使其依赖于接口,而不是具体实现类,达到Monther类不用修改的目的。

interface IReader {
    String getContent();
}

class Newspaper implements IReader {
    @Override
    public String getContent() {
        System.out.println("妈妈开始讲报纸上的内容");
        return "金融风暴卷土而来......";
    }
}

class Book implements IReader {
    @Override
    public String getContent() {
        System.out.println("妈妈开始讲书上的故事");
        return "很久很久以前......";
    }
}

class Monther {
    public void narrate(IReader reader) {
        System.out.println(reader.getContent());
    }
}

public class DIPClient {
    public static void main(String[] args) {
        Monther monther = new Monther();
        monther.narrate(new Book());
        monther.narrate(new Newspaper());
    }
}

输出结果:
妈妈开始讲书上的故事
很久很久以前......
妈妈开始讲报纸上的内容
金融风暴卷土而来......

Mother类与接口IReader发生依赖关系,而Book和Newspaper都属于读物的范畴,他们各自都去实现IReader接口,这样就符合依赖倒置原则了。

传递依赖关系有三种方式,以上的例子中使用的方法是接口传递,另外还有两种传递方式:构造方法传递和setter方法传递。

在实际编程中,我们一般需要做到如下3点:

  1. 低层模块尽量都要有抽象类或接口,或者两者都有。
  2. 变量的声明类型尽量是抽象类或接口。
  3. 使用继承时遵循里氏替换原则。
原文地址:https://www.cnblogs.com/lwcode6/p/13954925.html