设计模式六大原则之----依赖倒置原则

来源:互联网 发布:js onload 编辑:程序博客网 时间:2024/05/21 10:08

一、定义

高层模块不应该依赖于底层模块,二者都应该依赖其抽象,抽象不应该依赖细节,细节应该依赖抽象。

  • 抽象就是接口和抽象类;细节就是具体的实现类
  • 依赖倒置本质:通过抽象即接口或者抽象类,使各个类和模块间彼此独立,实现模块间的松耦合

友情提醒:xmind导出的图片有点模糊,请放大查看
这里写图片描述

二、 问题的由来

2.1 问题

类A直接依赖类B,假如要将类A改为依赖类C,那么必须修改类A来完成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是底层模块,负责基本的原子操作;修改类A有可能给程序带来不必要的风险。

2.2 解决方案

将类A修改为依赖接口I,让类B和类C各自实现接口I,类A通过接口I间接与类B和类C发生关系,这样就会大大降低类A修改的几率。

三、 优点

  • 减少类之间的耦合
  • 提高系统的稳定性
  • 降低并发风险
  • 提高代码的可读性

四、 依赖倒置注入的实现

  • 构造函数注入依赖对象
    • 通过构造函数参数声明依赖对象
  • Setter方法注入依赖对象
    • 通过Setter方法参数声明依赖对象
  • 接口注入依赖对象
    • 通过接口参数声明依赖对象

五、 注意

抽象类中已经实现的方法,子类最好不要覆盖

六、 使用场景

  • 依赖倒置在小项目中很难体现出来,是否遵循依赖倒置原则影响不大
  • 项目越大,需求改变越多,采用依赖倒置原则设计的接口或者抽象类约束实现类,维护成本会大大减少

七、 案例

妈妈讲故事,用代码描述出来。

7.1 实现需求

母亲类:

public class Mother {    private static final String TAG = "Mother";    public Book book;    public Mother(Book book) {        this.book = book;    }    public void tellStory(){        Log.e(TAG,"妈妈讲故事:"+book.getContent());    }}

Book类:

public class Book {    public String getContent() {        return "白雪公主和七个小矮人的故事";    }}

客户端调用:

    Book book = new Book();    Mother mother = new Mother(book);    mother.tellStory();

执行结果:

11-15 17:07:24.718 17891-17891/com.designpatterndisclipline E/Mother: 妈妈讲故事:白雪公主和七个小矮人的故事

7.2 增加需求

母亲不光能够读书,还能够读报纸

添加一个报纸类

public class NewsPaper {    public String getContent() {        return "今年的双十一又突破了几千亿了...";    }}

修改母亲类中的逻辑,让他能够读报纸

/** * Created by rytong on 2017/11/15. */public class Mother {    private static final String TAG = "Mother";    private Book book;    private NewsPaper newsPaper;    public void setBook(Book book) {        this.book = book;    }    public void setNewsPaper(NewsPaper newsPaper) {        this.newsPaper = newsPaper;    }    public void ReadBook(){        Log.e(TAG,"妈妈讲故事:"+book.getContent());    }    public void ReadNewsPaper(){        Log.e(TAG,"妈妈读报纸:"+newsPaper.getContent());    }}

修改客户端调用逻辑:

    Mother mother = new Mother();    mother.setBook(new Book());    mother.setNewsPaper(new NewsPaper());    mother.ReadBook();    mother.ReadNewsPaper();

执行结果:

11-15 17:16:00.568 23141-23141/? E/Mother: 妈妈讲故事:白雪公主和七个小矮人的故事11-15 17:16:00.568 23141-23141/? E/Mother: 妈妈读报纸:今年的双十一又突破了几千亿了...

在刚才的代码中,我们添加了新的新闻类,修改了Mother类,修改了客户端调用的地方。其中Mother类的修改有可能会影响之前读书的逻辑,是有隐患的。

7.3 遵循依赖倒置原则重构

让书、报纸等读物实现同一个接口

/** * Created by rytong on 2017/11/15. */public interface IBook {    public String getContent();}

报纸和故事书都实现该接口

public class Book implements IBook{    public String getContent() {        return "白雪公主和七个小矮人的故事";    }}public class NewsPaper implements IBook{    public String getContent() {        return "今年的双十一又突破了几千亿了...";    }}

修改母亲类中的逻辑

public class Mother {    private static final String TAG = "Mother";    private IBook iBook;    public void setiBook(IBook iBook) {        this.iBook = iBook;    }    public void read(){        if (iBook instanceof Book){            Log.e(TAG,"妈妈讲故事:"+iBook.getContent());        }        if (iBook instanceof NewsPaper){            Log.e(TAG,"妈妈读报纸:"+iBook.getContent());        }    }}

客户端调用:

    Mother mother = new Mother();    IBook iBook = new Book();    mother.setiBook(iBook);    mother.read();    iBook = new NewsPaper();    mother.setiBook(iBook);    mother.read();

执行结果:

11-15 17:32:17.890 32436-32436/com.designpatterndisclipline E/Mother: 妈妈讲故事:白雪公主和七个小矮人的故事11-15 17:32:17.891 32436-32436/com.designpatterndisclipline E/Mother: 妈妈读报纸:今年的双十一又突破了几千亿了...

这里这个简单的例子,简单的模仿了新需求增加时的一些逻辑处理,本例子也并没有完全遵循依赖倒置原则,在Mother中每次修改还需要加小部分逻辑,不过比之前已经好了很多了。需要注意一点:如果方法在抽象类中已经实现, 子类尽量不要覆盖该方法。