`
mingj
  • 浏览: 23206 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论
阅读更多
Dependency Injection 这个名词,是在 Martin Fowler 的《Inversion of Control Containers and the Dependency Injection pattern》文章之后才广为人知。在文章中,Martin 解释了当时初起流行的 IOC 概念:为了消除应用程序对插件实现的依赖,程序的主控权从应用程序移到了框架。为了让 IOC 概念不那么令人迷惑,Martin 把流行的几种 IOC Container 实现模式命名为 Dependency Injection Pattern(DIP,下文简称 DI 模式)。很明显,DI 的定义更准确形象,而且因为 Martin 在软件开发社区巨大的影响力,DI 模式以及 spring framework 作为其最成功的实现之一很快被软件开发社区接受并成为日常开发的必备利器。

“依赖注入”给我们代码的编写,特别是测试代码的编写带来了很多好处。借助于 DI 模式,我们可以将服务的依赖声明和具体实现相分离,通过配置不同的服务实例交给框架管理,来让应用程序获得良好的灵活性和柔性。

比如我们有这样的代码:

public void hello() {
	Foo foo = new Foo();
	foo.sayHello();
	//...
}


这种情况下,hello() 方法就依赖于 Foo 类的 sayHello() 方法实现,会给测试带来了很多的不稳定性,比如耗时、服务异常之类。如果依照 DI 模式来重构,这段代码就会变成这样:

public void hello(Foo foo) {
	foo.sayHello();
	//...
}


在测试时我们就可以使用行为确定符合预期的‘mock’的 Foo 类来代替实际的 Foo 类,从而将测试关注点聚集到 hello() 方法,也避免了 Foo 类 sayHello() 方法的具体实现对测试可能带来的影响。

以上就是 DIP 最常见也是最令人熟悉的一层涵义,从 IOC、spring framework 开始接受 DIP,自然而然会把 DIP 等同于 DI 模式,但其实它还有另一层涵义。在《Agile Software Developement:principles,Patterns,and Practices》的第11章 依赖倒置原则,给出了 DIP 的另一种解读:DIP —— 依赖倒置原则,这里的 DIP 就不再是 Dependency Injection Pattern(依赖注入模式),而是 Dependency Inversion Principle(依赖倒置原则,以下简称 DI 原则)。DI 原则主要包括下面两条启发式规则:
A. 高层模块不应依赖于低层模块。二者都应依赖于抽象
B. 抽象不应依赖于细节。细节应依赖于抽象

人们通常会用好莱坞法则来诠释启发规则 A:“Don't call us, we'll call you.”其中的 we/us,就是指高层模块,you 则是低层模块,高层模块来决定低层的模块,就像好莱坞制片人最终掌握着演员上镜与否的生杀大权。

我们来看看软件系统中常见的类关系,高层的 Policy Layer 使用了低层的 Mechanism Layer,而低层的 Mechanism Layer 又使用了更低层的 Utility Layer(见图1)

图1中的依赖关系是传递的:高层的 Policy Layer 对于其下一直到 Utility Layer 的改动都是敏感的,一旦我们修改了低层模块的实现,高层模块不得不也修改相应的实现来适应低层模块的修改。这种依赖关系,与软件复用的目标是相悖的,而且也不符合实际的业务变化。我们通常会需要切换低层的实现方式或者版本,而很少会去修改高层的业务。比如有一个证书申请发放系统,需要使用异步的消息机制来处理用户请求。客户可能会要求把底层的 MessageQueue 从 IIS 切换成 ActiveQ,但高层的证书申请发放的业务流程是稳定的,不会因低层基础服务的改变而作出修改。

所以,为了应付现实中的变化,也为了向高层屏蔽这些变化,我们通常会给低层模块抽象出接口,作为高层和低层之间的契约。这样,高层模块就应该只与低层模块的接口打交道,不再关心低层模块的实现细节。而低层模块的实现,我们可以通过配置文件由框架来切换,不影响到高层的行为。说到这里,大家应该可以看到 Spring 的影子了。此时类关系图就变成了下图(图2)的样子

嗯,现在的类关系已经遵循接口依赖了,甚至加上了框架的 DI 模式,系统也具有了一定的灵活性和易改变性,看起来很像大多数的系统了。但是,这是不是就是符合 DI 原则呢?我们可以看到,这里的接口通常是由低层模块来定义和派生,也就是低层模块抽象出来提供给外界调用的服务接口。高层模块其实还是依赖于低层模块,只是这次高层模块产生依赖的是低层模块里面声明的接口。想起曾经在 javaeye 上看到一篇帖子,作者抱怨为什么 java 不提供 extracts 关键字,这样就可以由框架或者容器在具体类上抽出接口定义(与 implements 对应),省得手工创建这些接口。或许这是目前依赖注入框架带来的误区吧:使用人员不理解 DIP 的本意,单纯是为框架的约束而创建。此时声明的类关系显然还是没有完全体现 DI 原则的精髓。

在书中 Robert 进一步对 DI 原则给出了解释:“请注意这里的倒置不仅仅是依赖关系的倒置,也是接口所有权的倒置。我们通常会认为工具库应该拥有它们自己的接口,但是应用 DIP (DI 原则)时,我们发现往往是消费者拥有抽象接口,而它们的服务者则从这些接口派生。”基于这种思路,前面例子更符合面向对象思想的层次关系图如下(图3)

图3和图2的区别主要在接口的所有权:高层模块应该拥有接口所有权,低层模块派生自高层模块里定义的接口。这就意味着:
1. 高层模块引用的接口定义应该和高层模块的其他类放在一起
2. 高层模块的复用是把高层拥有的所有类和接口定义作为一个整体来复用的
3. 接口定义的改变只有根据高层模块的需要才进行的,而不是低层模块

OK,直到现在,我们才能说 DI 原则原来是这个意思,否则,体会不到就有可能误人误己。Robert 在本章的结论中说“事实上,这种依赖关系的倒置正是好的面向对象设计的标志所在,使用何种语言来编写程序是无关紧要的。如果程序的依赖关系是倒置的,它就是面向对象的设计。如果程序的依赖关系不是倒置的,它就是过程化的设计。”信哉此言!

那么,要按照 OOA & OOD 的设计思路来进行系统设计,DI 原则对我们有什么帮助呢?其实,DI 原则不仅仅是抽象的原则,而且是可以启发推导出出种种具体的实践。我们来看看对 OOA & OOD 的帮助。因为依赖关系是倒置的,就可以通过对高层策略的抽象和定义驱动出低层服务者的接口。以此类推,直到把最底层的模块设计出为止。那什么是高层策略呢?怎么找出潜在的抽象?书中同样给出了答案:“它是应用背后的抽象,是那些不随具体细节的改变而改变的真理。它是系统内部的系统——它是隐喻(metaphore)”


References:
Inversion of Control Containers and the Dependency Injection pattern,http://www.martinfowler.com/articles/injection.html,Martin Fowler
控制反转与依赖注入模式,http://gigix.blogdriver.com/diary/gigix/inc/DependencyInjection.pdf,熊节 译
《Agile Software Developement:principles,Patterns,and Practices》,Robert Fowler 著,邓辉 译,孟岩 审

2
0
分享到:
评论

相关推荐

    DIP4DIP8DIP16 DIP24封装Altium Designer AD PCB封装库2D3D元件库文件.zip

    DIP4DIP8DIP16 DIP24封装Altium Designer AD PCB封装库2D3D元件库文件,25个DIP 3D视图封装,DIP_TP20-300 DIP_TP40-600 DIP4-300 DIP6-300 DIP8-300 DIP8-300_MH DIP14-300 DIP14-300_MH DIP16-300 DIP16-300_MH ...

    DIP直插封装库PCB文件3D封装AD库

    DIP4-300、DIP6-300、DIP8-300、DIP8-300_MH、DIP14-300、DIP14-300_MH、DIP16-300、DIP16-300_MH、DIP18-300、DIP18-300_MH、DIP20-300、DIP20-300_MH、DIP22-300、DIP22-300_MH、DIP24-300、DIP24-300_MH、DIP24-...

    常用芯片DIP类封装PCB封装库(AD库,封装带3D视图).zip

    常用芯片DIP类封装PCB封装库(AD库,封装带3D视图),Altium Designer的PCB封装库,.PcbLib格式的,带3D视图,非常实用。详细封装型号如下: Component Count : 25 Component Name --------------------------------...

    DIP封装Altium封装库AD三维视图PCB封装库(2D3D封装库)

    DIP_TP20-300 DIP_TP40-600 DIP4-300 DIP6-300 DIP8-300 DIP8-300_MH DIP14-300 DIP14-300_MH DIP16-300 DIP16-300_MH DIP18-300 DIP18-300_MH DIP20-300 DIP20-300_MH DIP22-300 DIP22-300_MH DIP24-300 DIP24-300_...

    设计模式沉思pdf

    《设计模式沉思录》是一本深入探讨软件设计模式的书籍,它旨在帮助开发者更好地理解和应用设计模式,提升代码质量和可维护性。设计模式是软件工程中的重要概念,是经过长期实践验证的解决常见问题的有效方案,它们...

    关于FLAC3D中dd,dip的一些理解

    在FLAC3D(Fast Lagrangian Analysis of Continua in 3 Dimensions)这款三维地壳动力学模拟软件中,dd和dip是两个用于定义几何平面的重要参数。这两个参数在地质建模中扮演着关键角色,尤其是在模拟岩层结构、断层...

    DIP系列元器件封装尺寸图

    本篇文章将详细解读“DIP系列元器件封装尺寸图”中的关键信息,帮助PCB工程师们理解并应用这些数据。 DIP,全称Dual In-line Package,中文名为双列直插式封装,是一种常见的电子元件封装形式,广泛应用于集成电路...

    国家医疗保障按病种分值付费(DIP)技术规范.pdf

    ### 国家医疗保障按病种分值付费(DIP)技术规范解析 #### DIP技术规范概述 按病种分值付费(Diagnosis-Intervention Packet,简称DIP)是一种创新的医保支付方式,旨在通过大数据分析实现更加合理、有效的医疗...

    DIP关于各噪声和滤波概述_dip_DIP滤波_

    在数字图像处理(DIP)领域,噪声和滤波是两个至关重要的概念,它们对于图像的质量和后续分析有着显著影响。本概述将深入探讨不同类型的噪声以及对应的滤波技术。 一、噪声类型 1. 高斯噪声:源于电子设备内部随机...

    DIP SOIC SOP TSOP TSSOP封装芯片 3D视图 3D模型库 (STEP后缀).rar

    DIP SOIC SOP TSOP TSSOP封装芯片 3D视图 3D模型库 (STEP后缀): DIP-20pin.STEP DIP-22pin.STEP DIP14.STEP DIP16.STEP DIP18.STEP DIP24-W.STEP DIP24.STEP DIP28-W.STEP DIP28.STEP DIP4.STEP DIP40.STEP DIP48....

    dip/px互转

    "dip"(density-independent pixels)和"px"(pixels)是Android中两种常用的尺寸单位,它们在屏幕适配和界面设计中起着关键作用。 **1. dpi与像素密度** 首先,我们需要理解dpi(dots per inch,每英寸点数)。dpi...

    DIP目录库(1.0版).xlsx

    DIP目录库(1.0版

    DSP lab0302 DIP

    在本文中,我们将深入探讨基于DSP(数字信号处理器)的实验室项目“Lab0302 DIP”,该实验主要涉及DIP(数字输入/输出)算法的运用,以实现对直流电机的精确控制,并通过LCD(液晶显示屏)进行实时数据显示。...

    FPGA核心板转DIP40-EP4CExE22 TO DIP40.rar

    标题"FPGA核心板转DIP40-EP4CExE22 TO DIP40.rar"揭示了这个压缩包内容是关于将FPGA(Field Programmable Gate Array,现场可编程门阵列)核心板转化为DIP40封装的转换过程,其中使用的FPGA型号为EP4CExE22。DIP40...

    常用的DIP28封装库,适用于Autium Designer 的元件常用PCB封装.zip

    DIP,即双列直插封装(Dual In-line Package),是一种常见的集成电路封装形式,它有两排引脚并平行排列,通常用于通过印刷电路板(PCB)的表面。而DIP28则意味着这种封装有28个引脚。 DIP封装在电子设计中非常重要...

    DIP_matlab图像处理_dip_

    "DIP_matlab图像处理_dip_"这个压缩包很显然包含了关于MATLAB图像处理的一些实践代码,可以帮助学习者理解和掌握图像处理的基本概念和方法。"dip"可能是指Digital Image Processing(数字图像处理)的缩写,是这个...

    DIP直插集成电路3D封装三维视图PCB封装库CAD Cadence AD库(Step后缀3D模型库).zip

    DIP直插集成电路3D封装三维视图PCB封装库CAD Cadence AD库(Step后缀3D模型库),包括如下: DIP-20pin.STEP DIP-22pin.STEP DIP14.PNG DIP14.STEP DIP16.PNG DIP16.STEP DIP18.PNG DIP18.STEP DIP20.PNG DIP20.step...

    AD DIP封装.rar

    《AD DIP封装库解析与应用》 在电子设计领域,Altium Designer是一款广泛使用的PCB(印制电路板)设计软件,它提供了强大的电路设计、布局和布线功能。"AD DIP封装.rar"是一个包含DIP封装库的压缩文件,对于进行PCB...

    DIP封装库芯片Altium库 AD元件库 PCB封装库 3D视图库(AD库+Protel库).zip

    DIP封装库Altium库 AD元件库 PCB封装库 3D视图库(AD库+Protel库) ,DIP4~DIP48共计25个封装,PcbLib后缀文件为AD 2D3D封装库,Lib后缀文件为Protel库,可以直接应用到你的项目设计中。

    DIP病种目录库(1.0版).xlsx

    DIP病种目录库Excel,可直接导入到数据库使用

Global site tag (gtag.js) - Google Analytics