C#实现六大设计原则之依赖倒置原则

依赖倒置原则(DIP)定义:

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

问题由来:

类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,

负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。

解决方案:

将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。

ps:

依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础

搭建起来的架构要稳定的多。抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定

好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。

依赖倒置原则的核心思想是面向接口编程,用一个例子来说明面向接口编程比相对于面向实现编程好在什么地方。

举个生活中的例子, 我们每天下班, 可能为了方便亦或锻炼身体, 选择骑单车回家, 对于现在单车, 又有各种各样: mobike, ofo, 小蓝, 小鸣等等...

(用C#代码表达)  定义3个实现, 分别对应的是各种单车品牌。

public class BlueGoGo
    {
        public void Go()
        {
            Console.WriteLine("骑的是小蓝单车");
        }
    }

    public class Ofo
    {
        public void Go()
        {
            Console.WriteLine("骑的是小黄单车");
        }
    }

    public class Mobike
    {
        public void Go()
        {
            Console.WriteLine("骑的是摩拜单车");
        }
    }

然后再定义一个骑行类(Ride)

//骑行类
    public class Ride
    {
        //扫码小蓝骑车
        public void ScanCodeByBlueGoGo()
        {
            BlueGoGo blue = new BlueGoGo();
            blue.Go();
        }

        //扫码摩拜单骑车
        public void ScanCodeByMoBike()
        {
            Mobike mo = new Mobike();
            mo.Go();

        }

        //扫码小黄骑车
        public void ScanCodeByOfo()
        {
            Ofo ofo = new Ofo();
            ofo.Go();
        }
    }

现在看上去确实也没什么问题, 所以调用一下, 一切正常

仔细看看, 这个代码确实有问题, 针对上面所讲的依赖倒置原则

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

正如例子中的 骑行类(Ride) 正是依赖BlueGoGo,MoBike,Ofo类, 如果现在 Ride类要新增一个小鸣单车, 则我们必须要修改Ride的代码,同是添加一个XiaoMing的类达到效果。

//骑行类
    public class Ride
    {
        //扫码小蓝骑车
        public void ScanCodeByBlueGoGo()
        {
            BlueGoGo blue = new BlueGoGo();
            blue.Go();
        }

        //扫码摩拜单骑车
        public void ScanCodeByMoBike()
        {
            Mobike mo = new Mobike();
            mo.Go();

        }

        //扫码小黄骑车
        public void ScanCodeByOfo()
        {
            Ofo ofo = new Ofo();
            ofo.Go();
        }

        /*
         * 该功能位新增的小鸣单车, 必须修改Ride类
         */
        public void ScanCodeByXiaoMing()
        {
            XiaoMing xming = new XiaoMing();
            xming.Go();
        }
    }

像MoBike, BlueGoGo, Ofo, XiaoMing 这些类, 他们都属于底层模块, 负责基本的骑车的动作,  按照依赖倒置的原则, 则不应该修改A, 否则如果在业务量很大的情况下, 则会给程序带来不必要的潜在风险。

用依赖导致的思想怎么去实现 新增小鸣单车而不对高层模块进行修改?

1.将单车的每个Go动作都抽象起来, 分别让他们去做各自的实现。

//修改位抽象的车类
    public abstract class abstarctBike
    {
        public abstract void Go();
    }

    public class BlueGoGo: abstarctBike
    {
        public override void Go()
        {
            Console.WriteLine("骑的是小蓝单车");
        }
    }

    public class Ofo : abstarctBike
    {
        public override  void Go()
        {
            Console.WriteLine("骑的是小黄单车");
        }
    }

    public class Mobike : abstarctBike
    {
        public override  void Go()
        {
            Console.WriteLine("骑的是摩拜单车");
        }
    }

    public class XiaoMing : abstarctBike
    {
        public override  void Go()
        {
            Console.WriteLine("骑的是小明单车");
        }
    }

这时, 我们再把骑行类(Ride)进行改造, 将原有的ScanCodeXXX 都弃用, 用一个全新的ScanCode提供一个抽象类型。

//骑行类
    public class Ride
    {
        //
        public void ScanCode(abstarctBike bike)
        {
            bike.Go();
        }

        /*
         *  以下位之前弃用的模式
         */

        //扫码小蓝骑车
        public void ScanCodeByBlueGoGo()
        {
            BlueGoGo blue = new BlueGoGo();
            blue.Go();
        }

        //扫码摩拜单骑车
        public void ScanCodeByMoBike()
        {
            Mobike mo = new Mobike();
            mo.Go();

        }

        //扫码小黄骑车
        public void ScanCodeByOfo()
        {
            Ofo ofo = new Ofo();
            ofo.Go();
        }

        /*
         * 该功能位新增的小鸣单车, 必须修改Ride类
         */
        public void ScanCodeByXiaoMing()
        {
            XiaoMing xming = new XiaoMing();
            xming.Go();
        }
    }

现在, 我们再调用, 只要指定给Ride对象ScanCode执行的类型就可以实现骑行动作。

这样修改后,无论以后怎样扩展单车类,都不需要再修改Ride类了。这只是一个简单的例子,实际情况中,代表高层模块的Ride类将负责完成主要的业务逻辑,

一旦需要对它进行修改,引入错误的风险极大。所以遵循依赖倒置原则可以降低类之间的耦合性,提高系统的稳定性,降低修改程序造成的风险。

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

  • 低层模块尽量都要有抽象类或接口,或者两者都有。
  • 变量的声明类型尽量是抽象类或接口。
  • 使用继承时遵循里氏替换原则。

依赖倒置原则的核心就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。

到此这篇关于C#实现六大设计原则之依赖倒置原则的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C#实现六大设计原则之单一职责原则

    单一职责(SRP)定义: 不要存在多于一个导致类变更的原因,通俗的说,即一个类只负责一项职责. 问题由来: 类T负责两个不同的职责:职责P1,职责P2.当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障. 解决方案: 遵循单一职责原则.分别建立两个类T1.T2,使T1完成职责P1功能,T2完成职责P2功能.这样,当修改类T1时,不会使职责P2发生故障风险:同理,当修改T2时,也不会使职责P1发生故障风险. ps: 说到单一职责原则,很多人都会不屑一顾.因为

  • 浅谈C#六大设计原则

    笔者作为一个菜鸟,会尝试以简单的代码和容易理解的语句去解释这几种原则的特性和应用场景. 这六种原则分别为单一职责原则.接口隔离原则.里氏替换原则.迪米特法则.依赖倒置原则.开闭原则. 单一职责原则 单一职责原则(SRP:Single responsibility principle),规定一个类中应该只有一个原因引起类的变化. 单一职责原则的核心就是解耦和增强内聚性. 问题: // 假设此类是数据库上下文 public class DatabaseContext { } public class

  • C#实现六大设计原则之接口隔离原则

    接口隔离原则(ISP)定义: 客户端不应该依赖它不需要的接口:一个类对另一个类的依赖应该建立在最小的接口上. 问题由来: 类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法. 解决方案: 将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系.也就是采用接口隔离原则. 举例来说明接口隔离原则: 类A依赖接口I中的方法1.方法2.方法3,类B是对类A依赖的实现. 类C依赖接口I中的方法1.方法4.

  • 六大设计原则之开闭原则

    定义: 一个软件实体如类.模块和函数应该对扩展开放,对修改关闭. 问题由来: 在软件的生命周期内,因为变化.升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试. 解决方案: 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化. 开闭原则是面向对象设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统. 开闭原则可能是设计模式六项原则中定义最模糊的一个了,它只告诉我们

  • C#实现六大设计原则之里氏替换原则

    定义: 1:如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子型. 2:所有引用基类的地方必须能透明地使用其子类的对象. 问题由来: 有一功能P1,由类A完成.现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成. 新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障. 解决

  • C#实现六大设计原则之迪米特法则

    定义: 一个对象应该对其他对象保持最少的了解. 问题由来: 类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大. 解决方案: 尽量降低类与类之间的耦合. PS: 自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚. 无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率. 低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的. 迪米特法则又叫最少知道原则,最早是在1987年由美国Northe

  • C#实现六大设计原则之依赖倒置原则

    依赖倒置原则(DIP)定义: 高层模块不应该依赖低层模块,二者都应该依赖其抽象:抽象不应该依赖细节:细节应该依赖抽象. 问题由来: 类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成.这种场景下,类A一般是高层模块, 负责复杂的业务逻辑:类B和类C是低层模块,负责基本的原子操作:假如修改类A,会给程序带来不必要的风险. 解决方案: 将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率. ps: 依赖倒置原则

  • PHP面向对象五大原则之依赖倒置原则(DIP)详解

    本文实例讲述了PHP面向对象五大原则之依赖倒置原则(DIP).分享给大家供大家参考,具体如下: 什么是依赖倒置呢?简单地讲就是将依赖关系倒置为依赖接口,具体概念如下: 1.上层模块不应该依赖于下层模块,它们共同依赖于一个抽象(父类不能依赖子类,它们都要依赖于抽象类) 2.抽象不能依赖于具体,具体应该要依赖于抽象. 注意,这里的接口不是狭义的接口. 为什么要依赖接口?因为接口体现对问题的抽象,同时由于抽象一般是相对稳定的或者是相对变化不频繁的,而具体是易变的.因此依赖抽象是实现代码扩展和运行期内绑

  • Java设计模式七大原则之依赖倒置原则详解

    目录 定义 案例 需求 方案一 方案二 对比分析 总结 定义 依赖倒转原则,又称依赖倒置原则(Dependence Inversion Principle),又称DIP原则,即:上层模块不应该依赖底层模块,它们都应该依赖于抽象,抽象不应该依赖于细节,细节应该依赖于抽象.抽象对代码来说即接口或者抽象类 细节对代码来说即实现类.换句话说 依赖倒转原则的核心的理念 相对于细节来说,抽象要稳定得多.要求我们 面向接口编程,进行设计. 案例 需求 工作人员接收微信老板发来的加班消息 方案一 定义工作人员W

  • 深入理解JavaScript系列(22):S.O.L.I.D五大原则之依赖倒置原则DIP详解

    前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第5篇,依赖倒置原则LSP(The Dependency Inversion Principle ). 英文原文:http://freshbrewedcode.com/derekgreer/2012/01/22/solid-javascript-the-dependency-inversion-principle/ 依赖倒置原则 依赖倒置原则的描述是: A. High-level modules should not

  • java面向对象设计原则之单一职责与依赖倒置原则详解

    目录 单一职责概念 实现 拓展 依赖倒置原则概念 示例 拓展 单一职责概念 不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,否则就应该把类拆分.交杂不清的职责将使得代码牵一发而动全身,导致代码混涩难懂,不易修改.难以扩展和复用.如:以前开发C/S程序中的胖客户端程序,就是将人机交互逻辑.业务加工处理逻辑和数据库操作逻辑混合在一起. 实现 单一职责原则是进行类的划分和封装的基本原则,进行类的具体抽象.尽量做到,类的功能单一和清晰化. 1.根据机能划分,使用封装来创建对象之间的分

  • 详解Java设计模式编程中的依赖倒置原则

    定义: 高层模块不应该依赖低层模块,二者都应该依赖其抽象:抽象不应该依赖细节:细节应该依赖抽象. 问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成.这种场景下,类A一般是高层模块,负责复杂的业务逻辑:类B和类C是低层模块,负责基本的原子操作:假如修改类A,会给程序带来不必要的风险. 解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率.          依赖倒置原则基于这样一个事实:

  • C#编程之依赖倒置原则DIP

    一.前言 我们先来看看传统的三层架构,如下图所示: 从上图中我们可以看到:在传统的三层架构中,层与层之间是相互依赖的,UI层依赖于BLL层,BLL层依赖于DAL层.分层的目的是为了实现“高内聚.低耦合”.传统的三层架构只有高内聚没有低耦合,层与层之间是一种强依赖的关系,这也是传统三层架构的一种缺点.这种自上而下的依赖关系会导致级联修改,如果低层发生变化,可能上面所有的层都需要去修改,而且这种传统的三层架构也很难实现团队的协同开发,因为上层功能取决于下层功能的实现,下面功能如果没有开发完成,则上层

  • 深入浅析python3 依赖倒置原则(示例代码)

    场景 针对园区停车信息,需要对各个公司提供的停车数据进行整合并录入自家公司的大数据平台 数据的录入无外乎就是对数据的增删改查 下面上一个常规的写法(未符合依赖倒置),整合来自 长安和丰田 的停车数据 class Changan(object): def __init__(self): self.type = 'changan' def ca_add(self): print('%s 新增' % self.type) def ca_del(self): print('%s 删除' % self.t

  • java的依赖倒置原则你了解多少

    目录 依赖倒置原则 案例: 背景: 1.面向实现编程 2.面向接口编程(简单版) 总结 依赖倒置原则 什么是依赖倒置原则: 高层模块不应该依赖低层模块,二者都应该依赖其抽象 抽象不应该依赖细节,细节应该依赖抽象 针对接口编程,不要针对实现编程 即: 每个类尽量继承自接口或者抽象类 优点:减少类之间的耦合,提高代码的稳定性,代码的可读性维护性. 案例: 背景: 现在有一个用户类叫Ggzx(也就是我),想要学习一些课程,简单的来实现调用学习的方法,然后在一个Test类之中输入学习的内容.但是我暂时只

  • java设计模式七大原则之依赖倒转原则详解

    目录 1.什么是依赖倒转原则? 2.代码案例 3.依赖关系传递的三种方式和案例举例 3.1 接口传递 3.2 构造方法传递 3.3 setter方法传递 4.依赖倒转原则总结 1.什么是依赖倒转原则? 高层模块不应该依赖低层模块,二者都应该依赖其抽象. 抽象不应该依赖细节,细节应该依赖抽象. 依赖倒转 (倒置) 的中心思想是面向接口编程. 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多.以抽象为基础搭建的架构比以细节为基础的架构要稳定的多.在Java中,抽象指的是接口

随机推荐