依赖倒置原则(DIP)

依赖倒置(Dependency Inversion Principle,缩写DIP)是面向对象六大基本原则之一。他是指一种特定的的解耦形式,使得高层次的模块不依赖低层次的模块的实现细节,依赖关系被颠倒(反转),从而使得低层次模块依赖于高层次模块的需求抽象.

该原则规定:

  • 高层次的模块不应该依赖低层次模块,二者都应该依赖其抽象接口.
  • 抽象接口不应该依赖于具体实现,而具体实现则应该依赖于抽象接口.

通过如下一个简单的示例,我们来看一下,我们通过一个简单地下单流程向我们的用户发送相关的短信或者邮件.


public SendingEmail
{
public void Send(string message){
//do something
}
} public Ordering
{
SendingEmail _sendingEmail=null;
public void Order(string message){
//Order business operation
if(_sendingEmail == null)
{
_sendingEmail=new SendingEmail();
}
_sendingEmail.Send(message);
}
}

这样看我们的代码没问题,目前只要我们完成了订单操作那么,那么则会触发发送功能,但是他却违反了DIP,因为Ordering类依赖于SendingEmail类,而SendingEmail类不是抽象类,而是一个具体的类.那我们再来想一个如果这时候业务口的人过来向我们提出了一个新的需求,要求我们改为短信而不是Email,那么我们需要怎么改?

public class SendingSMS
{
public void Send(string message){
//do something
}
}
public Ordering
{
SendingEmail _sendingEmail=null;
SendingSMS _sendingSMS=null;
bool isSendingSMS=true;
public void Order(string message){
//Order business operation
if(isSendingSMS){
if(_sendingSMS == null)
{
_sendingSMS=new SendingSMS();
}
_sendingSMS.Send(message);
}else{
if(_sendingEmail == null)
{
_sendingEmail=new SendingEmail();
}
_sendingEmail.Send(message);
} }
}

根据上述需求我们不得不创建更多的类,并且在Ordering类中声明他,最后我们还需要使用IF ELSE语句来决定使用SMS还是使用电子邮件.但是当我们有更多这种处理操作后,那么可能比现在还混乱,这就意味着我们必须在Ordering类中声明更多新的具体类的实例.

我们需要抽离出来一种方式,让高级模块去依赖于抽象,用它来代替我们实现类,该抽象将映射到实现类.

控制反转(IoC)

控制反转(Inversion of Control,缩写为IOC)是面向对象中的设计原则,他可以帮助我们使高层模块依赖于抽象,而不是底层模块的具体实现.换句话说,他有助于实现(依赖倒置原则——DIP).

public interface ICustomerCommunication
{
void Send(string message);
}

然后我们修改SendingEmailSendingSMS类以从ICustomerCommunication接口继承.

public class SendingEmail:ICustomerCommunication
{
public void Send(string message){
//do something
}
} public class SendingSMS:ICustomerCommunication
{
public void Send(string message){
//do something
}
}

我们再来修改一下Ordering类以使用该抽象接口

public Ordering
{
ICustomerCommunication _customerComm=null;
bool isSendingSMS=true;
public void Order(string message){
//Order business operation
if(isSendingSMS){
if(_customerComm == null)
{
_customerComm=new SendingSMS();
}
_customerComm.Send(message);
}else{
if(_customerComm == null)
{
_customerComm=new SendingEmail();
}
_customerComm.Send(message);
} }
}

通过如上修改我们做的控制反转更符合DIP.现在我们的高级模块只需要依赖于抽象,而不用去依赖实现.

依赖注入(DI)

依赖注入(Depeondency Injection,缩写为DI)是实现控制反转的一种方式.常用的依赖注入方法有3种:

  • 构造函数注入
  • 方法注入
  • 属性注入

虽然说通过上面代码我们实现了IoC,并且Ordering类依赖于ICustomerCommunication抽象,但我们仍然在Ordering类中使用了实现类,这使用我们无法在类于类之间完全解耦.

  if(isSendingSMS){
if(_customerComm == null)
{
_customerComm=new SendingSMS();
}
_customerComm.Send(message);
}else{
if(_customerComm == null)
{
_customerComm=new SendingEmail();
}
_customerComm.Send(message);
}

那我们再来说说DI,DI主要帮助我们将实现注入到抽象的类(ICustomerCommunication接口)中.DI的主要减少类之间的耦合,并且将抽象和具体实现的绑定移除依赖类.

  • 构造函数注入

    通过构造函数注入我们将实现类的对象传递给依赖类的构造函数,并将其分配给这个接口.
public class Ordering
{
ICustomerCommunication _customerComm=null;
public Ordering(ICustomerCommunication customerComm){
_customerComm=customerComm;
}
public void Order(string message){
_customerComm.Send(message);
}
}

在上面的代码中,构造函数将采用实现类对象绑定到接口中.如果我们将SendingSMS的实现传递给这个类,我们要做的就是声明一个SendingSMS类的实例,然后将其传递给Ordering的构造函数,如下所示:

SendingSMS sendingSMS=new SendingSMS();
Ordering ordering=new Ordering(sendingSMS);
ordering.Order("msg");
  • 方法注入

    通过使用构造函数注入,我们将不得不在Ordering类的生存期内使用实现类的实例SendingSMS或SendingEmail类.现在如果要在每次调用该方法时传递实现类的实例,则必须使用方法注入.

public class Ordering
{
public void Order(ICustomerCommunication customerComm,string message){
_customerComm=customerComm;
_customerComm.Send(message);
}
}

调用方式如下所示

SendingSMS sendingSMS=new SendingSMS();
Ordering ordering=new Ordering(sendingSMS);
ordering.Order(sendingSMS,"msg");
  • 属性注入

通过如上描述我们知道了构造函数注入方法在整个生命周期中使用依赖类,而方法注入是将我们的注入直接去限于该方法中,然后我们再去了解一下属性注入


public class Ordering
{
public ICustomerCommunication customerComm {get;set;}
public void Order(string message){
_customerComm.Send(message);
}
}

调用方式如下所示

SendingSMS sendingSMS=new SendingSMS();
Ordering ordering=new Ordering(sendingSMS);
ordering.customerComm=sendingSMS;
ordering.Order("msg");

其实构造函数注入是实现DI最常用的方法.如果需要在每个方法调用上传递不同的依赖关系,则可以使用方法注入属性注入的使用还是比较少的.

Reference

https://zh.wikipedia.org/wiki/控制反转

https://zh.wikipedia.org/zh-hans/依赖反转原则

.NET IoC模式依赖反转(DIP)、控制反转(Ioc)、依赖注入(DI)的更多相关文章

  1. java依赖的斗争:依赖倒置、控制反转和依赖注入

    控制反转(Inversion Of Controller)的一个著名的同义原则是由Robert C.Martin提出的依赖倒置原则(Dependency Inversion Principle),它的 ...

  2. 依赖倒置原则DIP&控制反转IOC&依赖注入DI

    依赖倒置原则DIP是软件设计里一个重要的设计思想,它规定上层不依赖下层而是共同依赖抽象接口,通常可以是上层提供接口,然后下层实现接口,上下层之间通过接口完全透明交互.这样的好处,上层不会因依赖的下层修 ...

  3. PHP依赖倒置和控制反转

    判断代码的好坏,我们有自己的标准:高内聚,低耦合.为了解决这一问题,php中有许多优秀的设计模式,比如工厂模式,单例模式. 而在代码中体现出来的设计模式,就如依赖注入和控制反转. 那什么是依赖注入? ...

  4. IoC(Inversion of Control 控制反转)

    控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度.其中最常见的方式叫做依赖注入(Dependency Inject ...

  5. [.net 面向对象程序设计深入](26)实战设计模式——使用Ioc模式(控制反转或依赖注入)实现松散耦合设计(1)

    [.net 面向对象程序设计深入](26)实战设计模式——使用IoC模式(控制反转或依赖注入)实现松散耦合设计(1) 1,关于IOC模式 先看一些名词含义: IOC: Inversion of con ...

  6. [.net 面向对象程序设计深入](31)实战设计模式——使用Ioc模式(控制反转或依赖注入)实现松散耦合设计(1)

    [.net 面向对象程序设计深入](31)实战设计模式——使用IoC模式(控制反转或依赖注入)实现松散耦合设计(1) 1,关于IOC模式 先看一些名词含义: IOC: Inversion of con ...

  7. 轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI) 依赖注入和控制反转的理解,写的太好了。

    轻松学,浅析依赖倒置(DIP).控制反转(IOC)和依赖注入(DI) 2017年07月13日 22:04:39 frank909 阅读数:14269更多 所属专栏: Java 反射基础知识与实战   ...

  8. IoC模式(依赖、依赖倒置、依赖注入、控制反转)

    1.依赖 依赖就是有联系,有地方使用到它就是有依赖它,一个系统不可能完全避免依赖.如果你的一个类或者模块在项目中没有用到它,恭喜你,可以从项目中剔除它或者排除它了,因为没有一个地方会依赖它.下面看一个 ...

  9. 个人对【依赖倒置(DIP)】、【控制反转(IOC)】、【依赖注入(DI)】浅显理解

    一.依赖倒置(Dependency Inversion Principle) 依赖倒置是面向对象设计领域的一种软件设计原则.(其他的设计原则还有:单一职责原则.开放封闭原则.里式替换原则.接口分离原则 ...

  10. 【转载】浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)

    原文地址 http://blog.csdn.net/briblue/article/details/75093382 写这篇文章的原因是这两天在编写关于 Dagger2 主题的博文时,花了大量的精力来 ...

随机推荐

  1. stand up meeting 11/16/2015

    第一周,熟悉任务中~ 大致写下一天的工作: 冯晓云:熟悉bing接口,本意是调在线的必应词典API,参阅了大量C#调用API开发.net的工作,[约莫是因为有个窗口互动性更强,所以这样的工作更有趣,也 ...

  2. 深入浅出node.js游戏服务器开发1——基础架构与框架介绍

    2013年04月19日 14:09:37 MJiao 阅读数:4614   深入浅出node.js游戏服务器开发1——基础架构与框架介绍   游戏服务器概述 没开发过游戏的人会觉得游戏服务器是很神秘的 ...

  3. 批量重命名脚本(Python)

    便携的批处理脚本,代码如下: import os import sys def rename(): path=input("请输入路径(例如D:/picture):") name= ...

  4. SpringBoot系列(九)单,多文件上传的正确姿势

    SpringBoot系列(九)分分钟解决文件上传 往期推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列(三)配 ...

  5. 吃瓜的正确姿势,Python绘制罗志祥词云图

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 这篇文章中向大家介绍了Python绘制词云的方法,不难看出绘制词云可以说是一 ...

  6. pytorch seq2seq闲聊机器人加入attention机制

    attention.py """ 实现attention """ import torch import torch.nn as nn im ...

  7. Java 虚拟机中的运行时数据区分析

    本文基于 JDK1.8 阐述分析 运行过程 我们都知道 Java 源文件通过编译器编译后,能产生相应的 .Class 文件,也就是字节码文件.而字节码文件通过 Java 虚拟机中的解释器,编译成特定机 ...

  8. 数据挖掘入门系列教程(十点五)之DNN介绍及公式推导

    深度神经网络(DNN,Deep Neural Networks)简介 首先让我们先回想起在之前博客(数据挖掘入门系列教程(七点五)之神经网络介绍)中介绍的神经网络:为了解决M-P模型中无法处理XOR等 ...

  9. Linux磁盘修复命令----fsck

    使用fsck命令修复磁盘时 一定要进入单用户模式去修复 语 法fsck.ext4[必要参数][选择参数][设备代号] 功 能fsck.ext4 命令: 针对ext4型文件系统进行检测 参数  -a 非 ...

  10. centos7源码安装Apache及Tomcat

    源码安装Apache (1) 一.通过 https://apr.apache.org/  下载 APR 和 APR-util 通过 http://httpd.apache.org/download.c ...