寂然解读设计模式 - 依赖倒置原则

I walk very slowly, but I never walk backwards 

设计模式原则 - 依赖倒转原则


寂然

大家好,我是寂然~,本节课呢,我来给大家介绍设计模式原则之依赖倒转原则,话不多说,我们直接进入正题,老规矩,首先带大家了解一下依赖倒转原则的官方定义,并作一个解释,然后我们通过案例代码来具体分析

官方定义

依赖倒转原则,又称依赖倒置原则(Dependence Inversion Principle),又称DIP原则,官方定义为:

High level modules should not depend upon low level modules. Both should depend upon abstractions.

(上层模块不应该依赖底层模块,它们都应该依赖于抽象)

Abstractions should not depend upon details. Details should depend upon abstractions.

(抽象不应该依赖于细节,细节应该依赖于抽象)

基本介绍

依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多,以抽象为基础搭建的架构要比以细节为基础的架构稳定的多,在Java中,抽象指的是接口或抽象类,细节指具体的实现类,即要求程序要依赖于抽象接口,不要依赖于具体实现


使用接口或者抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成

换句话说,接口或者抽象类的价值就在于设计

案例演示 - 钉钉消息

OK,那我们假设有这样一个场景,有一个工作人员,每天要接收钉钉工作消息,简易代码如下图所示

//工作人员
class Worker {
    public void getMessage(Dingding dingding){
        dingding.sendMessage();
    }
}
//钉钉类
class Dingding{
    public void sendMessage(){
        System.out.println("钉钉上老板喊你加班呢");
    }
}
//测试代码
public class InversionDemo{
    public static void main(String[] args) {
        new Worker().getMessage(new Dingding());
    }
}

案例分析

OK,可以看到上面的代码,对案例场景进行了简易的实现,那我们就对上面的代码进行分析

现在是获取钉钉消息,如果我们获取的对象是微信,短信等,那根据上面的实现,我们需要新增类,同时Worker类也要增加相应的接收方法,这样显然维护性很差的同时,频繁修改代码也会带来意想不到的风险


那我们就想到了上面依赖倒转原则的定义,既然接口或者抽象类的价值就在于设计,我们可以引入一个接口来指定规范,表示信息的接收者,因为微信,钉钉都属于消息接收的途径,那就可以让他们实现该接口,来完成具体的细节,然后Worker与该接口发生依赖,使用依赖倒转的思想,完成代码改造

解决方案

OK,那根据上面的思路,我们对案例代码进行改造,改造后的代码示例如下:

//定义接口,指定接收消息的规范
interface IMessage{
    public void sendMassage();
}
//钉钉消息
class Dingding implements IMessage{
    @Override
    public void sendMassage() {
        System.out.println("钉钉上老板喊你开会啦");
    }
}
//微信消息
class Weixin implements IMessage{
    @Override
    public void sendMassage() {
        System.out.println("微信上老板喊你加班啦");
    }
}
//工作人员
class Worker {
    //这里是我们对于接口的依赖
    public void getMessage(IMessage message){
        message.sendMassage();
    }
}
//简易测试代码
public class Inversion01Demo{
    public static void main(String[] args) {
        new Worker().getMessage(new Dingding());
        new Worker().getMessage(new Weixin());
    }
}

案例总结

可以看到,通过上面改造过的代码,我们定义了接口作为模板,让各种消息接收的途径去实现,这样的话如果业务要求还需要接收短信的消息,添加短信类实现接口即可,工作人员端不需要进行变动和维护,其实依赖倒置原则的本质上就是通过抽象(抽象类或接口)使各个类或模块实现彼此独立,不互相影响,实现模块间的松耦合

一句话:依赖倒置原则的核心就是面向抽象(抽象类或者接口)编程,大家要明白,以抽象为基准搭建起来的架构比以细节为基准搭建的架构要稳定的多,因此,在拿到需求之后,要先顶层再细节的方式来进行代码设计

依赖关系传递方式

当我们进行依赖倒转的时候,一定要进行依赖关系的传递,接着,我们简单聊聊依赖关系的三种传递方式

一、通过接口传递

使用接口传递,示例代码如下

//通过接口方式传递
interface IMessage{
    public void sendMassage(Produce produce); //抽象方法,接收接口
}

interface Produce{
    void produceMessage();
}

//工作人员
class Worker implements IMessage{

    @Override
    public void sendMassage(Produce produce) {
        produce.produceMessage();
    }
}

可以看到,Worker实现了接口 IMessage,实现了里面的方法,该方法参数接收了 Produce 接口类型,这就是一个典型的使用接口传递的案例,抽象方法sendMassage() 里面传的是一个接口

二、通过构造方法传递

使用构造方法传递,示例代码如下

//通过构造器方式传递
interface IMessage{
    public void sendMassage(); //这里没有接收接口
}

interface Produce{
    void produceMessage();
}

class Worker implements IMessage{

    public Produce produce; //属性为Produce类型

    public Worker(Produce produce) { //构造器来传递
        this.produce = produce;
    }

    @Override
    public void sendMassage() {
        this.produce.produceMessage();
    }

}

可以看到,这种方式,将Produce置为 Worker类的属性,值通过构造器来传递,赋给 this.produce ,然后在sendMassage()方法里通过 this.produce 来调用目标方法

三、通过setter()方法传递

使用setter()方法传递,实例代码如下

//通过setter方式传递
interface IMessage{
    public void sendMassage();
}

interface Produce{
    void produceMessage();
}

class Worker implements IMessage{

    public Produce produce; //属性为Produce类型

    public void setProduce(Produce produce) {
        this.produce = produce;
    }

    @Override
    public void sendMassage() {
        this.produce.produceMessage();
    }
}

可以看到,这种方式,将Produce置为 Worker类的属性,值通过setter()方法来传递,赋给 this.produce ,然后在sendMassage()方法里通过 this.produce 来调用目标方法produceMessage()

注意事项&细节

  • 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好
  • 变量的声明类型尽量是抽象类或者接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化
  • 继承时遵循里式替换原则(下章内容来详细介绍)

下节预告

OK,下一节,我们正式进入设计模式原则之里式替换原则的学习,我会为大家用多个案例分析,来解读设计模式原则之里式替换原则,以及它的注意事项和细节,最后,希望大家在学习的过程中,能够感觉到设计模式的有趣之处,高效而愉快的学习,那我们下期见~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351