OOP、DI、IOC的关系

时间:2015-11-12 17:36:09   收藏:0   阅读:303

此随笔的重点在“Demo分析”一章,以代码的分阶段变化讲述了DI,DIP,IOC的演变,写在前面文字均为铺垫。

希望各位园友拍砖,促使流浪者的进步,现在有很多问题想讨论,即以此文寻找志同道合的园友,另此文草草写作,发现越写越觉得还有很多没有描述出来,暂且如此,等待过些时日再来总结,草草之作难免有错误之处,忘各位园友斧正,不甚感激!

先来个简介,看不懂没关系,按着顺序看下去等Demo看完就明白了,简介只是为了有个宏观的概念,强烈建议看完Demo分析后再重读一遍:

1. DI: 有了OO,抽象封装为一个个的类,这样类之间便有了依赖关系,为了使各个组件(类)之间解耦(将new实例转移出类),不在“高层组件”内初始化“低层组 件”,而是通过各种方式将“低层组件”注入到“高层组件”内,依赖注入(Dependency Injection)出现了。

2. DIP: 依赖倒置原则(Dependency Inversion Principle),何为依赖倒置?下面几步是关键(首先假设我们有“高层组件”和“低层组件”两个类库DLL):

 

    1)第一阶段,“高层组件”包含(Has-A)“低层组件”。没有注入,“高层组件”严重依赖“低层组件”。

   

    2)第二阶段,“高层组件”包含(Has-A)“低层组件”的抽象父类或接口,接口在“低层组件”中定义。构造方法,接口,或Set属性实现注入,“高层组件”与“低层组件”耦合转到抽象上,解除了耦合。此时客户端调用时仍然使用new关键字实现“低层组件”并注入“高层组件”。

 

    3)第三阶段,“高层组件”包含(Has-A)“低层组件”的抽象父类或接口,接口在“高层组件”中定义。构造方法,接口,或Set属性实现注入,“高层组件”与“低层组件”耦合转到抽象上,解除了耦合。此时客户端调用时仍然使用new关键字实现“低层组件”并注入“高层组件”。

 

    4)第四阶段,“高层组件”包含(Has-A)“低层组件”的抽象父类或接口,接口被提取成DLL接口库中定义。构造方法,接口,或Set属性实现注入,“高层组件”与“低层组件”耦合转到抽象上,解除了耦合。此时客户端调用时仍然使用new关键字实现“低层组件”并注入“高层组件”。

 

DIP的原则就是要“低层组件”依赖“高层组件”,后来被衍生为“低层组件”和“高层组件”均依赖于抽象。通过以上几个阶段可以看出:

第一阶段——“高层组件”依赖“低层组件”(这时当“低层组件”未完成时,“高层组件”并不能实现,因为“高层组件”强烈依赖“低层组件”)。

第二阶段——虽然有了抽象并依赖抽象,但仍然是“高层组件”依赖“低层组件”。接口由“低层组件”定义,如果“低层组件”未抽象出接口,“高层组件”不能实现,“高层组件”依然依赖“低层组件”。

第三阶段——为了使“低层组件”依赖“高层组件”(此时出现了依赖反转),将接口放在了“高层组件”,也就是说“高层组件”定义了接口,而“低层组件”去实现。此时可以先设计“高层组件”然后实现“低层组件”。此时实现了“高层组件”和“低层组件”的解耦。

第四阶段——DIP在第三阶段已经完成,然而人们确发现,在客户端(掉用“高层组件”和“低层组件”的程序集)总得new出一个“低层组件”来注入“高层组件”,这样客户端中出现的耦合,能不能把这种耦合也消除了那?“配置文件”+“反射”的IOC框架实现了这个愿望,在“配置文件”里实现了“接口”和“实现”的映射,不用再用new来实例化并注入了,这样也就出现了所谓的IOC框架。

3. IOC:控制反转(Inversion of Control),其实就是DIP的实现。此时客户端仍然在使用new关键字实 现“低层组件”并注入“高层组件”,虽然“高层组件”和“低层组件”解耦了,但是在客户端那里确依然存在耦合,这时以“配置文件”+“反射”的IOC框架 出现了,在代码中找不到new关键字,因为接口对应的实现已经在配置文件中,IOC框架将接口和实现进行了自动装配(这个时候IOC解耦的强大就很容易看 出来了,如果我是架构师,那么此时我只需要将“接口”DLL和“高层组件设计出来”即可,不需要有任何的“低层组件”实现,然后我把“接口”DLL扔给程 序员,让他们去实现“接口”,等程序员完成后,我不用修改任何代码,只需要把实现“接口”的DLL放入执行目录,并将IOC配置文件中的“接口”和程序员实现的DLL进行一下Mapping即可)。

解耦,是一门很迷人的艺术,简介到此,下面开始正题。 

基础:

一、有一个(Has-A),是一个(Is-A)区别:

继承是面向对象的基础,但是如果继承的层次太深使得逻辑复杂不好掌控。

有很多OO大神都提倡多用“组成”(Has-A)少用“继承”(Is-A),我感觉还是有一定道理的,当时少用并不代表不用,个人感觉父类为抽象类或接口最为有用,有一些设计模式就是靠继承来达到优美的Code的,如模板方法,策略模式等等......

二、面向接口编程(面向抽象编程):

此处的接口并不是说Interface,而是指抽象,包括Interface和abstract class。

1. 传统菜鸟Code

public class IntelCPU
{
    //...
}

IntelCPU intelCPU = new IntelCPU(); //传统Code

2. 面向接口编程

public abstract class CPU
{
    //...
}

public class IntelCPU : CPU
{
    //...
}

public class AMDCPU : CPU
{
    //...
}
复制代码

CPU cpu = new IntelCPU(); //面向接口编程

CPU cpu = new AMDCPU(); //面向接口编程

此时CPU是个abstract class,而IntelCPU和AMDCPU都是它的子类。

三、解耦的发展史:

面向对象->面向接口->配置文件动态装配

相当于:

依赖具体类->依赖抽象->依赖抽象+配置文件动态组装“抽象”和“实现”

深入:

1. DI(Dependency Injection)依赖注入

个人认为所谓的依赖注入,可拆分成“依赖”和“注入”。

依赖:

对于组件A和组件B,A组件中包含B组件,这时就是Has-A的关系,也就是说A组件和B组件之间存在“依赖”。

注入:

此时讨论的均是B组件不是系统自定义的“元数据类型”的情况,即B组件不做为局部变量使用。(元数据类型:string,int,double 等等都属于元数据,与其他类不产生依赖关系)

1)对于非面向接口编程(也就是说B不存在抽象父类):

一般会在A组件的构造方法中初始化B组件的一个实例,此时并未从外界注入。

public class A
{
    private B _b;
    
    public A()
    {
       _b = new B();
    }
}

public class B
{
}
复制代码

2)对于面向接口编程(也就是说B存在抽象父类)

原文:http://www.cnblogs.com/tianciliangen/p/4959491.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!