• 转:什么是DIP、IoC、DI



    DIP
    依赖倒置原则DIP(Dependency-Inversion Principles)

    IoC
    控制反转(Inversion of Control,IoC),简言之就是代码的控制器交由系统控制,而不是在代码内部,通过IoC,消除组件或者模块间的直接依赖,使得软件系统的开发更具柔性和扩展性。控制反转的典型应用体现在框架系统的设计上,是框架系统的基本特征,不管是.NET Framework抑或是Java Framework都是建立在控制反转的思想基础之上。

    控制反转很多时候被看做是依赖倒置原则的一个同义词,其概念产生的背景大概来源于框架系统的设计,例如.NET Framework就是一个庞大的框架(Framework)系统。在.NET Framework大平台上可以很容易地构建ASP.NET Web应用、Silverlight应用、Windows Phone应用或者Window Azure Cloud应用。很多时候,基于.NET Framework构建自定义系统的方式就是对.NET Framework本身的扩展,调用框架提供的基础API,扩展自定义的系统功能和行为。然而,不管如何新建或者扩展自定义功能,代码执行的最终控制权还是回到框架中执行,再交回应用程序。黄忠诚先生曾经在Object Builder Application Block一文中给出一个较为贴切的举例,就是在Window From应用程序中,当Application.Run调用之后,程序的控制权交由Windows Froms Framework上。所以,控制反转更强调控制权的反转,体现了控制流程的依赖倒置,所以从这个意义上来说,控制反转是依赖倒置的特例。

    DI
    依赖注入(Dependency Injection,DI),早见于Martin Flower的Inversion of Control Containers and the Dependency Injection pattern一文,其定义可概括为:

    客户类依赖于服务类的抽象接口,并在运行时根据上下文环境,由其他组件(例如DI容器)实例化具体的服务类实例,将其注入到客户类的运行时环境,实现客户类与服务类实例之间松散 的耦合关系。

    常见的三种注入方式
    简单而言,依赖注入的方式被总结为以下三种。

    接口注入(Interface Injection),将对象间的关系转移到一个接口,以接口注入控制。

    首先定义注入的接口:

    public interface IRunnerProvider
    {
    void Run(Action action);
    }
    为注入的接口实现不同环境下的注入提供器,本例的系统是一个后台处理程序提供了运行环境的多种可能,默认情况下将运行于单独的线程,或者通过独立的Windows Service进程运行,那么需要为不同的情况实现不同的提供器,例如:

    public class DefaultRunnerProvider : IRunnerProvider
    {
    public void Run(Action action)
    {
    var thread = new Thread(() => action());
    thread.Start();
    }
    }


    对于后台服务的Host类,通过配置获取注入的接口实例,而Run方法的执行过程则被注入了接口所定义的逻辑,该逻辑由上下文配置所定义:

    public class RunnerHost : IDisposable
    {
    IRunnerProvider provider = null;

    public RunnerHost()
    {
    // Get Provider by configuration
    provider = GetProvider(config.Host.Provider.Name);
    }

    public void Run()
    {
    if (provider != null)
    {
    provider.Run(() =>
    {
    // exceute logic in this provider,
    if provider is DefualtRunnerProvider,
    // then this logic will run in a new thread context.
    });
    }
    }
    }


    接口注入,为无须重新编译即可修改注入逻辑提供了可能,GetProvider方法完全可以通过读取配置文件的config.Host.Provider.Name内容,来动态地创建对应的Provider,从而动态地改变BackgroundHost的Run()行为。

    构造器注入(Constructor Injection),客户类在类型构造时,将服务类实例以构造函数参数的形式传递给客户端,因此服务类实例一旦注入将不可修改。

    public class PicWorker
    {
    }

    public class PicClient
    {
    private PicWorker worker;

    public PicClient(PicWorker worker)
    {
    // 通过构造器注入
    this.worker = worker;
    }
    }


    属性注入(Setter Injection),通过客户类属性设置的方式,将服务器类实例在运行时设定为客户类属性,相较构造器注入方式,属性注入提供了改写服务器类实例的可能。

    public class PicClient
    {
    private PicWorker worker;

    // 通过属性注入
    public PicWorker Woker
    {
    get { return this.worker; }
    set { this.worker = value; }
    }
    }


    关系
    总体而言,DIP、IoC还有DI之间有着剪不断理还乱的关系,其中DIP是对于依赖关系的理论总结,而IoC和DI则体现为具体的实践模式。IoC和DI为消除模块或者类之间的耦合关系提供了有效的解决方案,从而保证了依赖于抽象和稳定模块或者类型,也就意味着坚持了DIP原则的大方向。


    而IoC和DI之间的区别主要体现在关注场合的不同:IoC强调控制权的反转作用,着眼于流程控制的场合;而DI则关注层次与层次、组件与组件、模块与模块或者类型与类型之间的"倒置",体现为设计模型上的依赖模式解构。

    这篇短文基本上是改编自Martin Fowler的Inversion of Control Containers and the Dependency Injection pattern,目的呢,是让读者能够在最短时间内了解IoC的概念。这也是我一贯的“风格”:最短的文字、最精要的内容、最清晰的说明。希望我能做到,自勉^_^

    在J2EE应用开发中,经常遇到的问题就是:如何将不同的组件组装成为一个内聚的应用程序?IoC模式可以解决这个问题,其目标是将组件的配置与使用分离开。

    IoC,Inversion of Control,控制反转[1],其原理是基于OO设计原则的The Hollywood Principle:Don't call us, we'll call you。也就是说,所有的组件[2]都是被动的(Passive),所有的组件初始化和调用都由容器负责。组件处在一个容器当中,由容器负责管理。

    要说明IoC模式最好的方法是使用代码。下边是一段正常的代码。

    class ClassA...

    public String aMethod(String arg){

    String result = instanceOfClassB.bMethod();

    do something;

    return result;

    }

    在上边的代码里,我们要解决的问题是:ClassA如何获得ClassB的实例?一个最直接的方法是在aMethod里声明:

    IClassB instanceOfClassB = new ClassB();

    这里使用了一个接口IClassB。

    问题是,如果出现这样的情况:继续使用ClassA,但要求用IClassB的另一个实现ClassB2代替ClassB呢?更概括一点说:ClassA怎样才能找到IClassB的具体实现?很明显,上述代码增加ClassA和ClassB的耦合度,以致于无法在不修改ClassA的情况下变更IClassB的具体实现。

    IoC模式就是用于解决这样的问题。当然,还有其他的方法,比如Service Locator模式,但现在我们只关注IoC。如前所述,IoC容器负责初始化组件(如IClassB),并将实例交给使用者。使用代码或配置文件以声明的方式将接口与实例关联起来,IoC容器负责进行实际的调用处理。对于调用者,只需要关注接口就行了。

    根据实例传入方式的不同,IoC分为type 1 IoC(接口注入[3])、type 2 IoC(设值方法注入)和type 3 IoC(构造子注入)。分别用代码说明如下:

    type 1 IoC(接口注入)

    public interface GetClassB {

    void getClassB(IClassB instanceOfClassB);

    }

    class ClassA implements GetClassB…

    IClassB instanceOfClassB;

    void getClassB(IClassB instanceOfClassB) {

    this.instanceOfClassB = instanceOfClassB;

    }

    type 2 IoC(设值方法注入)

    class ClassA...

    IClassB instanceOfClassB;

    public void setFinder(IClassB instanceOfClassB) {

    this.instanceOfClassB = instanceOfClassB;

    }

    type 3 IoC(构造子注入)

    class ClassA…

    ClassB instanceOfClassB;

    public classA(IClassB instanceOfClassB) {

    this. instanceOfClassB = instanceOfClassB;

    }

    Spring使用的是type 2 IoC。

    参考:
    http://leshy.iteye.com/blog/69034
    http://book.51cto.com/art/201108/284974.htm
    http://baike.baidu.com/view/1486379.htm
    http://www.cnblogs.com/xugang2008/archive/2011/07/06/2098889.html
    http://www.cnblogs.com/winsonet/archive/2010/02/09/1666204.html
    http://www.cnblogs.com/n-pei/archive/2011/02/15/1955460.html

  • 相关阅读:
    深入理解JavaScript系列(17):面向对象编程之概论
    深入理解JavaScript系列(16):闭包(Closures)
    深入理解JavaScript系列(15):函数(Functions)
    深入理解JavaScript系列(14):作用域链(Scope Chain)
    深入理解JavaScript系列(13):This? Yes,this!
    深入理解JavaScript系列(12):变量对象(Variable Object)
    深入理解JavaScript系列(11):执行上下文(Execution Contexts)
    深入理解JavaScript系列(10):JavaScript核心(晋级高手必读篇)
    深入理解JavaScript系列(9):根本没有“JSON对象”这回事!
    深入理解JavaScript系列(8):S.O.L.I.D五大原则之里氏替换原则LSP
  • 原文地址:https://www.cnblogs.com/xiaohouye/p/11158714.html
Copyright © 2020-2023  润新知