关于控制反转(Inversion of Control),在具体实现上也有许多其它的叫法,如依赖倒置(Dependency Inversion Principles, DIP)、依赖注入(Dependency Injection)等等,现在自己就本人的理解,来说一下这里的反转及倒置的讲究。

就总的原则来说,控制反转(依赖倒置)是:

高层模块不应该依赖于低层模块,二者都要依赖于抽象

抽象不应该依赖于具体,具体应该依赖于抽象。

从现实社会生活中,我们知道:官职越大的人,负责的工作越抽象,官职越低的人,负责的工作越具体;所以说,上面动动嘴皮子,下面跑断腿。也就是说,正常状态下,应该是高层调用(控制)低层办理具体的事务,我们估且说这是正置吧;如果低层调用(控制)高层进行工作,这种情况可以说是倒置了。

软件设计中,确实存在有正置和倒置。正置是结构化设计时的情况,自上而下,由高层调用低层完成软件设计,低层的具体工作向高层提供服务,而且IT中许多设计思想也是正置的,比如计算机网络的七层(或五层)架构,物理层向上层数据链路层提供服务(下层向上层提供服务,上层调用或控制或依赖下层),数据链路层向上层网络层提供服务,等等。

但在面向对象的设计中,为了降低模块间的耦合,实际低耦合,高内聚的总原则,控制反转就成为面向对象设计的主要原则。

看下例:

    public class EmailService
{
public void SendMessage()
{....}
} public class NotificationSystem
{
private EmailServie svc;
public NotificationSystem()
{
svc = new EmailService();
} public void InterestingEventHappened()
{
svc.SendMessage();
}
}

这是典型的正置,类EmailService属于具体类(低层模块),发出邮件;而NotificationSystem类则是调用类(高层模块),它调用(控制)具体类EmailService中的具体方法完成任务,这在面向对象设计中,是一种紧耦合。
这种紧耦合可能会造成如下问题:

1、当两个模块其中之一产生修改时,另外一个模块就会受到影响。如果多个模块紧耦合,这个影响就有可能造成软件系统修改量巨大,如果是封装出售的商业模块,对购买者今后的平滑升级与维护将造成极大困难。

2、如果这时NotificationSystem要发出的消息不是电邮,而是短信或者存到数据库中以后再看,这个类就需要再进行修改。

为解决这个问题,就需要把紧耦合变为松耦合,办法就是:加入一个抽象层,大家都依赖于抽象层,因为抽象层是最不容易变化的,从某种程序上来讲,设计完成之后,抽象层应该永远不变,如果需要,可以再添加其它抽象层。

这个抽象层可以是接口(Interface)或者抽象类(Abstract Class),但一般建议使用接口完成。

   public interface IMessagingService
{
void SendMessage();
}
public class EmailService : IMessagingService
{
public void SendMessage()
{....}
} public class NotificationSystem
{
private IMessagingService svc;
public NotificationSystem()
{
svc = new EmailService();
} public void InterestingEventHappened()
{
svc.SendMessage();
}
}

看上面的代码,添加了一个接口IMessagingService。然后高层模块NotificationSytem依赖于抽象的接口,低层模块EmailService实现这个抽象接口内的方法定义(或称虚方法、虚函数);事实是两者都依赖于这个抽象接口。低层模块EmailService通过实现接口方法定义完成对它的依赖(即EmailService : IMessagingService),而高层模块NotificationSystem则通过声明接口IMessagingService(抽象层)实现了对它的调用(依赖),而这个调用实际是调用的更高层,因为抽象层是最高层,这种调用,就是控制反转或者依赖倒置。实际调用的是它的具体实现,而这个具体实现由EmailService中的SendMessage方法完成。

这里只是谈了控制反转或者依赖倒置的总原则。对于依赖倒置的具体办法,其它文章再谈。

----------

因为还有一种情况没有解决,如果现在要加入发短信的功能,应该有类似下面的代码

    public class SmsService : IMessagingService
{
public void SendMessage()
{....}
}

但只加入下面的代码,还没有完成发短信的功能,需要修改NotificationSystem类中构造函数的实现了,它写的是静态的实例化EmailService对象。因此解决办法应该是:在构造函数中(或者实现方法InterestingEventHappened()中)加入传递参数,这个传递参数就是具体的低层模块类(或具体实现模块类)实例化的对象,这样就不需要修改主程序代码,实现了对修改封闭,对扩展开放的OO设计原则。

控制反转(Inversion of Control)之我的理解的更多相关文章

  1. 控制反转Inversion of Control (IoC) 与 依赖注入Dependency Injection (DI)

    控制反转和依赖注入 控制反转和依赖注入是两个密不可分的方法用来分离你应用程序中的依赖性.控制反转Inversion of Control (IoC) 意味着一个对象不会新创建一个对象并依赖着它来完成工 ...

  2. 控制反转 (inversion of control)

    The inversion of control (IoC) pattern is abstract; it says that one should move dependency creation ...

  3. IOC-控制反转(Inversion of Control),也成依赖倒置(Dependency Inversion Principle)

    基本简介 IoC 亦称为 “依赖倒置原理”("Dependency Inversion Principle").差不多所有框架都使用了“倒置注入(Fowler 2004)技巧,这可 ...

  4. 设计模式之————依赖注入(Dependency Injection)与控制反转(Inversion of Controller)

    参考链接: 依赖注入(DI) or 控制反转(IoC) laravel 学习笔记 —— 神奇的服务容器 PHP 依赖注入,从此不再考虑加载顺序 名词解释 IoC(Inversion of Contro ...

  5. Inversion of Control 控制反转 有什么好处

    作者:Mingqi链接:https://www.zhihu.com/question/23277575/answer/169698662来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转 ...

  6. Spring 控制反转容器(Inversion of Control – IOC)

    系列教程 Spring 框架介绍 Spring 框架模块 Spring开发环境搭建(Eclipse) 创建一个简单的Spring应用 Spring 控制反转容器(Inversion of Contro ...

  7. Spring学习3—控制反转(IOC)Spring依赖注入(DI)和控制反转(IOC)

    一.思想理解 Spring 能有效地组织J2EE应用各层的对象.不管是控制层的Action对象,还是业务层的Service对象,还是持久层的DAO对象,都可在Spring的 管理下有机地协调.运行.S ...

  8. 控制反转和spring在项目中可以带来的好处

    Spring实例化Bean的三种方式分别是: 1,xml配置使用bean的类构造器 <bean id="personService" class="cn.servi ...

  9. 控制反转(IoC)与依赖注入(DI)

    1.控制反转(Inversion of Control)与依赖注入(Dependency Injection) 控制反转即IoC (Inversion of Control),它把传统上由程序代码直接 ...

  10. 控制反转(IoC)-解析与实现

    控制反转(Inversion of Control)缩写:IoC是面向对象编程中框架级别里的一个重要的概念, 可以说Spring框架的核心就是基于IoC原理的. 这个概念到底是什么呢? 这么讲吧,一个 ...

随机推荐

  1. 一天天的sql总结

    一. 多张表之间的查询: join/inner join  on inner join 是比较运算符,只返回符合条件的行. left/outer join on 左外连接包含left join左表所有 ...

  2. 第五百八十二天 how can I 坚持

    好吧,是我错了,昨天,做好自己就行了,别人怎么样是别人的事,永远保持一颗单纯向上的心. 时间过得真快,明天又周六了.. 睡觉.

  3. Reactjs 入门基础(二)

    如果我们需要向组件传递参数,可以使用 this.props 对象,实例如下: <body> <div id="example"></div> & ...

  4. ERROR SparkUncaughtExceptionHandler: Uncaught exception in thread

    ERROR SparkUncaughtExceptionHandler: Uncaught exception in thread Thread[appclient-registration-retr ...

  5. 关于CacheLookup一个有趣的问题

    今天写一个与其他系统进行物料同步的接口,通过COM Business Connector调用Axapta3.0的方法将数据插入到物料表中,中间发生异常,事务回滚,再次调用的时候提示刚刚发生异常的物料已 ...

  6. redis cluster搭建

    一 .准备文件: Ruby :http://www.ruby-lang.org/en/downloads/    redis-3.0.5.tar.gz Redis:http://www.redis.c ...

  7. python学习笔记1-元类__metaclass__

    type 其实就是元类,type 是python 背后创建所有对象的元类   python 中的类的创建规则: 假设创建Foo 这个类 class Foo(Bar): def __init__(): ...

  8. globals()

    [globals() ] globals() Return a dictionary representing the current global symbol table. This is alw ...

  9. 【C#】 目前的技能点

    [C#] 目前的技能点 一. C/S 1. WinForm 2. WPF 二.B/S 1. MVC 2. ASPX 3. WebService 4. js 5. jQuery , jQuery UI ...

  10. java.io.Serializable 序列化问题

    java.io.Serializable 序列化问题 Person.java package a.b.c; public class Person implements java.io.Seriali ...