设计模式之初识IoC/DI(六)

本篇和大家一起学习IoC和DI即控制反转和依赖注入。

当然听上去这词语非常的专业,真不知道是怎么组出来的,看上去难归看上去难,但稍微理解一下也就这么回事了。

首先我们要明白IoC/DI干嘛用的,不然别人问“你知道IoC/DI吗?”,“知道啊!”,“那干嘛用的?”,呵呵,就郁闷了,其实IoC/DI的作用就两个字——解耦,说一千道一万,还是离不开这两个字。所以,IoC/DI的作用就是解耦。

至于为什么要解耦,解耦在面向对象开发中的重要性在这里就不做细说了。

下面我们来认识一下它们的真面目吧!

要理解IoC/DI两个概念,就必须搞清楚如下问题:

  • 参与者都有谁?
  • 依赖:谁依赖于谁?为什么需要依赖?
  • 注入:谁注入谁?到底注入了什么?
  • 控制反转:谁控制谁?控制什么?为什么叫反转?有没有正向呢?正向又是什么?
  • 依赖注入和控制反转是同一概念吗?

下面来简要回答一下上述问题,把这些问题搞明白了,也就明白了IoC/DI。

  1. 参与者都有谁:一般有三方参与,一个是某个对象,一个是IoC/DI容器,还有就是某个对象的外部资源。

    注:某个对象指任意一个普通的对象,IoC/DI容器简单说就是用来实现IoC/DI功能的一个框架程序,比如现在有许多IoC/DI框架(Unity、Spring.NET、Autofac),对象的外部资源指对象需要的,但是从对象外部获取的,都统称资源,比如对象需要其他对象。

  1. 谁依赖于谁:当然是某个对象依赖于IoC/DI容器
  2. 为什么需要依赖:因为对象需要IoC/DI的容器来提供对象需要的外部资源。
  3. 谁注入谁:IoC/DI的容器注入某个对象
  4. 到底注入了什么:就是注入某个对象所需要的外部资源,说完整点就是IoC/DI的容器将某个对象所需要的外部资源注入了该对象。
  5. 谁控制谁:IoC/DI的容器来控制对象
  6. 控制什么:主要是控制对象实例的创建
  7. 为什么叫反转:反转是相对与正向而言的。
  8. 正向又是什么:通常情况下的应用程序,如果在A里使用C,一般我们会直接创建C的对象,也就是说,在A类中主动获取所需要的外部资源,这种情况被成为正向的,那什么是反向呢?就是A类不再主动去获取C,而是被动等待,等待IoC/DI的容器获取一个C的实例,然后反向地注入到A类中。 

先看没有IoC/DI的时候,常规A类使用C的示意图

 

 

当有了IoC/DI的容器后,A类不再主动去创建C了,而是被动等待,等待IoC/DI的容器获取C的实例,然后反向注入到A类中。

 

依赖注入和控制反转是同一概念吗?

根据上面讲述,依赖注入和控制反转是对同一件事情的不同描述。依赖注入使用应用程序的角度去描述,我们可以把依赖注入描述的完整点:应用程序依赖容器创建并注入它所需要的外部资源。控制反转是从容器的角度去描述的,描述的完整点就是:容器控制应用程序,由容器反向的向应用程序注入其所需要的外部资源。

好了,通过上述学习,大家都应该对IoC/DI有了基本了解,下面我们再来正式认识下依赖注入和控制反转。

依赖注入中,依赖关系就像被注入的液体,我们可以在任何时候将依赖关系注入到模块中,而不只局限于编译是绑定。这种依赖关系是通过注入的方式完成的,就意味着我们可以随时更新,因为注入的液体与模块本身并无直接关联,举个不恰当的比喻,我们难道天生就要和针筒打交道吗?

实现依赖注入的前提是面向接口编程,辅助的技术可以使用反射技术。

MF.将依赖注入的形式分为三种:构造函数注入、设置方法注入和接口注入。

接口注入是通过定义接口约束的方式实现依赖注入,会给容器带来设计的限制。

而构造函数注入与设置方法(可以是属性)注入则是使用类的构造函数以及自定义的setter方法或属性进行依赖注入(关于这两点,可以参考第一篇《设计模式之UML类图的常见关系(一)》中的聚合和组合关系)。

在这里我们也把第一篇聚合和组合关系类图拿到了。

下面这个是使用Setter方法/属性实现依赖注入的。说白了就是定义一个Setter方法/属性,在该方法/属性内将对象关联起来。

下面这个是构造函数的依赖注入,就是将Setter方法/属性换成了构造函数,但是请仔细看看两幅图片的代码和注入,什么时候使用Setter方法/属性注入,什么时候使用构造函数注入,当时在开发过程中,通常是使用构造函数进行依赖注入的。

还有一个就是接口注入。

这是我认为最不够优雅的一种依赖注入方式。要实现接口注入,首先ServiceProvider要给出一个接口定义:

public interface InjectFinder {
    void injectFinder(MovieFinder finder);
}

接下来,ServiceUser必须实现这个接口:

class MovieLister: InjectFinder
{
      private MovieFinder finder;
      public void injectFinder(MovieFinder finder) {
      this.finder = finder;
    }
}

为什么现在介绍IoC/DI呢?

想想,前面我们已经学习了三种工厂,那能不能将工厂和IoC/DI联系起来呢?好了,下面就自己想想!

小结:IoC/DI对编程带来的最大改变不是在代码上,而是在思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动了,被动的等待IoC/DI容器来创建并注入它所需要的资源。

 

posted @ 2013-11-15 13:25  烧点饭  阅读(2327)  评论(1编辑  收藏  举报