依赖倒置原则

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. 任何类都不应该从具体类派生
  4. 尽量不要重写基类已经写好的方法(里式替换原则)
  5. 结合里式替换原则来使用: 结合里式替换原则和依赖倒置原则我们可以得出一个通俗的规则,接口负责定义public属性和方法,并且声明与其他对象的依赖关系,抽象类负责公共构造部分的实现,实现类准确的实现业务逻辑,同时在适当的时候对父类进行细化。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值