依赖倒转原则(Dependency Inversion  Principle, DIP):抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对抽象(接口)编程,而不是针对实现细节编程。

开闭原则(OCP)是面向对象设计原则的基础也是整个设计的一个终极目标,而依赖倒置原则(DIP )则是实现OCP原则的一个基础,换句话说开闭原则(OCP)是你盖一栋大楼的设计蓝图,那么依赖倒置原则就是盖这栋大楼的一个钢构框架,没有钢构架构是很难顺利盖起一栋大楼的,同样的在面向对象软件设计的过程中不遵守依赖倒置原则是很难开发出符合开闭原则的软件的。更不用说开发出易于维护,易于升级的软件。 因此开闭原则是非常重要的一个原则,它有很强的实操性,并且能够直接指导我们写代码代码。

通常要符合这个原则的第一步就是针对抽象编程,类之间的依赖关系尽量去使用高层抽象不要使用底层的实现细节,从软件工程来说高层抽象是较稳定的,也就是说抽象具有一定的稳定性,而实现细节较不稳定,也就是说实现细节具有易变性,而我们期望软件具有更好的稳定性,显而易见我们在开发的时候自然而然的要走稳定路线(依赖抽象编程)。这个原则也是对软件工程中要求“高聚低偶”实践一个保障和指导。

我们来看一个例子假设我们在开发一个软件产品需要一个日志系统,要将系统产生的一些重要事情记录在记事本上。通常我们的实现如下:

    public class Logger{public void Info(string infoText){Console.WriteLine($"[{DateTime.Now}][Info]:{infoText}");}public void Debug(string debugText){Console.WriteLine($"[{DateTime.Now}][Debug]:{debugText}");}public void Warn(string warmText){Console.WriteLine($"[{DateTime.Now}][Warm]:{warmText}");}public void Error(string errorText,Exception exception){Console.WriteLine($"[{DateTime.Now}][Error]:{errorText} - Exception:{exception.Message}");}}

客户端调用如下:

    static void Main(string[] args){Logger logger = new Logger();logger.Info("This is a info text.");logger.Debug("This is a debug text.");logger.Warn("This is a warn text.");logger.Error("This is a error text", new Exception("This is a exception."));Console.ReadKey();}

输出:

这看起来还不错,一切都是那么自然。但是随着时间的推移,产品做的好买了很多客户,产品变得越来越大,使用Logger 类的地方成千上万处,可怕的事情终于发生了:

A 客户提出来我想把日志存在数据库中便于做统计分析。

B 客户说我想把日志打印在一个控制台上便于我时时监测系统运行情况。

C 客户说我要把日志存到Windows Azure  Storage上。

。。。。

客户越来越多奇葩需求不断涌出。我们的产品变得很难修改,很难维护,很难去适合所有的客户。 怎么办呢? 回过头来看看我们的这个日志系统的设计才恍然大悟:没有遵守面向对象设计原则的依赖倒置原则和开闭原则了。知道就好,找到法门了, 我们将日志这一块的设计重构一下让其符合OCP和DIP应该就可以了。 那么我们就要首先抽象写日志的接口ILog, 让实际调用的地方调用高层抽象(ILog),具体的实现类TextLogger,ConsoleLogger,DatabaseLogger,AzureStorageLogger都继承自ILog接口,然后我们在利用反射加配置,不同的用户配置不同的具体实现类,这样问题就迎任而解了。 看代码:

    public interface ILog{void Info(string infoText);void Debug(string debugText);void Warn(string warmText);void Error(string errorText, Exception exception);}public class TextLogger:ILog{public void Info(string infoText){Console.WriteLine($"[{DateTime.Now}][Info]:{infoText}");}public void Debug(string debugText){Console.WriteLine($"[{DateTime.Now}][Debug]:{debugText}");}public void Warn(string warmText){Console.WriteLine($"[{DateTime.Now}][Warm]:{warmText}");}public void Error(string errorText,Exception exception){Console.WriteLine($"[{DateTime.Now}][Error]:{errorText} - Exception:{exception.Message}");}}public class DatabaseLogger:ILog{public void Info(string infoText){Console.WriteLine($"[{DateTime.Now}][Info]:{infoText}");}public void Debug(string debugText){Console.WriteLine($"[{DateTime.Now}][Debug]:{debugText}");}public void Warn(string warmText){Console.WriteLine($"[{DateTime.Now}][Warm]:{warmText}");}public void Error(string errorText,Exception exception){Console.WriteLine($"[{DateTime.Now}][Error]:{errorText} - Exception:{exception.Message}");}}public class ConsoleLogger:ILog{public void Info(string infoText){Console.WriteLine($"[{DateTime.Now}][Info]:{infoText}");}public void Debug(string debugText){Console.WriteLine($"[{DateTime.Now}][Debug]:{debugText}");}public void Warn(string warmText){Console.WriteLine($"[{DateTime.Now}][Warm]:{warmText}");}public void Error(string errorText,Exception exception){Console.WriteLine($"[{DateTime.Now}][Error]:{errorText} - Exception:{exception.Message}");}}public class AzureStorageLogger:ILog{public void Info(string infoText){Console.WriteLine($"[{DateTime.Now}][Info]:{infoText}");}public void Debug(string debugText){Console.WriteLine($"[{DateTime.Now}][Debug]:{debugText}");}public void Warn(string warmText){Console.WriteLine($"[{DateTime.Now}][Warm]:{warmText}");}public void Error(string errorText,Exception exception){Console.WriteLine($"[{DateTime.Now}][Error]:{errorText} - Exception:{exception.Message}");}}

添加一个配置在Config中:

    <appSettings><add key="Logger" value="ConsoleApp1.TextLogger"/></appSettings>

客户端的调用改成调用ILog:

    static void Main(string[] args){string key = ConfigurationManager.AppSettings["Logger"];ILog logger = ObjectBuildFactory<ILog>.Instance(key);logger.Info("This is a info text.");logger.Debug("This is a debug text.");logger.Warn("This is a warn text.");logger.Error("This is a error text", new Exception("This is a exception."));Console.ReadKey();}

输出:

A客户期望将日志写在数据库中只需要将配置改成下面这样就可以了:

    <appSettings><add key="Logger" value="ConsoleApp1.DatabaseLogger"/></appSettings>

根据不同的客户需求只需要改这个配置的value值就可以了。

要使上面的代码顺利运行我们要加一个辅助类用于反射:

public class ObjectBuildFactory<T>
{public static T Instance(string key){Type obj = Type.GetType(key);if (obj == null) return default(T);T factory = (T)obj.Assembly.CreateInstance(obj.FullName);return factory;}
}

那么有一天E客户说他们公司有自己的日志系统并开发了一套日志分析工具,他们可以开放API让我们把日志直接存到他们的日志系统中去。 这次好办了啊,只需要定义一个具体类继承自ILog接口并实现所有的方法,在每一个实现的方法中调用客户的API, 最后将实现的类配置到配置文件中就可以很好的满足客户的要求了, 这样是不是很完美呢?我们完全遵守了DIP和OCP原则,也很好的使用了LSP,使得我们软件变得稳定,应对需求的变化变得简单了,也易于升级和易于维护了。

在使用DIP是需要注意一下几点

1. 继承自高层接口的类要实现所有接口中的方法。

2.子类中除了接口的方法,在用接口声明的对象调用的地方是无法被调用到的。除非直接调用子类,但是直接调用子类是违背DIP的。

3. DIP是实现OCP的重要原则保障,一般违背了DIP很难不违背OCP,可以看这一篇【面向对象设计原则】之开闭原则(OCP)。

4.LSP 是实现DIP的基础,多态给实现DIP提供了可能。 可以看这一篇 【面向对象设计原则】之里氏替换原则(LSP)。

转载于:https://www.cnblogs.com/vaiyanzi/p/6904449.html

【面向对象设计原则】之依赖倒置原则(DIP)相关推荐

  1. 7.12 其他面向对象设计原则3: 依赖倒置原则DIP

    其他面向对象设计原则3: 依赖倒置原则DIP  The Dependency Inversion Principle 7.1 依赖倒置原则DIP The Dependency Inversion P ...

  2. 面向对象设计原则-03依赖倒置原则

    面向对象设计原则-03依赖倒置原则 依赖倒置原则的定义 依赖倒置原则(Dependence Inversion Principle,DIP)是 Object Mentor 公司总裁罗伯特·马丁(Rob ...

  3. 软件设计原则之里氏替换原则、依赖倒置原则

    系列文章目录 软件设计原则之单一职责原则.开闭原则 软件设计原则之里氏替换原则.依赖倒置原则 软件设计原则之接口隔离原则.合成复用原则.迪米特原则 文章目录 系列文章目录 一.里氏替换原则 什么是里氏 ...

  4. 3.六大原则例子-- 依赖倒置原则(DIP)例子

    设计模式六大原则例子-- 依赖倒置原则(DIP)例子 之前我们对设计模式的六大原则做了简单归纳,这篇博客是对依赖倒置原则进行的举例说明. 依赖倒置原则的意义 DIP是6大原则中最难以实现的原则,它是实 ...

  5. 设计原则 单一职责原则、开放封闭原则、依赖倒置原则、里氏代换原则、迪米特法则

    目录 1 单一职责原则 2 开放封闭原则 3 依赖倒置原则 4 里氏代换原则 5 迪米特法则 1 单一职责原则 比如:电脑内存坏了就应该更换内存,不应该更换CPU (内存负责内存.CPU负责CPU) ...

  6. 设计模式六大原则之里氏替换原则、依赖倒置原则详解

    设计模式六大原则--里氏替换原则.依赖倒置原则详解 1.里氏代换原则(Liskov Substitution Principle) 概念 顾名思义,该原则用于经常发生替换的地方,在Java中指的是实现 ...

  7. 面向对象七大设计原则之依赖倒置原则

    熟练掌握和应用面向对象设计七大原则,是初中级工程师向高级/资深工程师进阶的一个必备技能.他可以大大的提升程序的可复用性和可维护性是重构代码的一大利器. 本人菜鸟一枚本面文章的出发点是1.加深个人印象. ...

  8. 设计模式六大原则之--依赖倒置原则(DIP)

    1. 依赖倒置原则,(Dependence Inversion Principle, DIP ) 定义:High level modules should not depend upon low le ...

  9. 【软件架构设计原则】开闭原则和依赖倒置原则

    文章目录 软件架构设计原则 开闭原则 依赖倒置原则 最后 软件架构设计原则 本文通过实例来讲解 开闭原则 依赖导致原则 开闭原则 开闭原则(Open-Close Principle,OCP)是指一个软 ...

  10. 六大设计原则之依赖倒置原则07

    目录 1.概述 2.业务场景 3.运用设计原则前代码实现 3.1.代码实现 3.2.总结 4.运用设计原则后代码实现 4.1.代码实现 4.2.总结 1.概述 依赖倒置原则(Dependence In ...

最新文章

  1. 类似c 的语言,类似C的脚本语言 little-lang
  2. Laravel 源码解读:php artisan make:auth
  3. java %2f_字符串“http%3A%2F%2F”转换成http://详解
  4. usb转rj45_笔记本接口不够怎么办?先看USB-A接口能转谁
  5. mysql proxy 管理_ProxyMySQL的Admin管理接口
  6. oracle conneciton properties,在WAS Liberty连接池中,我可以验证借用连接吗?
  7. 关于读入带空格的字符串与输入输出重定向
  8. MYSQL优化派生表(子查询)在From语句中的
  9. 面向对象开发方法概述
  10. 以行为单位对字符串变量下标为奇数位置上的字符按其ASCii值从小到大的顺序进行排序,排序后的结果仍按行重新存入字符串数组xx中
  11. 如何体验4G极限速度?一部Mate30系列5G轻松搞定
  12. TCP协议三次握手/四次挥手
  13. 马健计算机博士,马健-中国科学院大学-UCAS
  14. 最近工作上的两个问题的调查报告
  15. matlab中关闭mexfunction,mex文件的运行时Matlab自动关闭
  16. android m是什么单位,M代表什么单位?
  17. Laplacian算子-Log算子-Dog算子边缘检测原理合集及实现
  18. 5G应用技术系列 - 从带宽和时延看5G和4G对应用区别
  19. 深度搜索和广度搜索特点的深刻理解
  20. MS Office for Mac的Clipart错误

热门文章

  1. 利用有序队列寻找最大的K个数
  2. vs 中程序被锁定的问题
  3. 批量获取客户端时间偏差
  4. generator tar.gz file in windows
  5. 回顾一个考务系统的开发
  6. mysql打开数据库控制台_在控制台中操作MYSQL数据库步骤以及一些小问题
  7. Lambda表达式与委托
  8. nginx创建n个工作子进程
  9. uat测试用例怎么写_测试用例怎么写?
  10. python setdefault函数_Python字典(Dictionary)setdefault()函数理解