昶萌

控制反转和服务容器
依赖注入模式:在面向接口编程时,控制反转和依赖倒置的实现方式。程序控制权在我们目前最常用的冯·诺依曼结构的计算机中...
扫描右侧二维码阅读全文
16
2018/01

控制反转和服务容器

依赖注入模式:
在面向接口编程时,控制反转和依赖倒置的实现方式。

程序控制权

在我们目前最常用的冯·诺依曼结构的计算机中,控制器是处理核心中非常重要的一部分。 计算机对数据的处理,都是在控制器的操控下完成的。 这种流程控制逻辑,也影响到了我们在程序的编程实现中。 在我们常用的面向过程、面向对象等包含控制操作的编程中,控制语句都是编程语言中最重要的一部分。 通过在编程中加入控制思想,可以让程序更好的适应计算机的处理方式。

1.png

所以,我们对程序的编写和运行,实际上是通过我们所编写的指令,操作计算机进行数据计算。

在常规思维的指导下,我们按控制处理去编写程序,就会关系每一个相关类的创建和它依赖类的创建。 这种我们通过操作一个类,驱动所有类的过程,就很像钟表里齿轮的运转,需要精细的进行控制。

2.png

然而,这种程序结构设计,在面对越来越庞大和复杂的场景时,就会使得齿轮的数量越来越庞大。 驱动类型各不相同,数量如此庞大的齿轮体系,如履薄冰,一点微小的偏差,都会使整个程序轰然崩塌。 而维护这样的结构,并在此基础上增加新的功能,就完全靠开发者的知识和经验的积累了。

当然,对于这些情况,我们也有另一种选择,就是使用框架。

框架与控制反转

对于任何一个业务型的框架来说,我们使用它的目的,都是为了简化开发的过程。 而大多数这类框架为我们简化开发过程的方式,都是通过将业务所需的功能进行封装,使我们对这些功能的调用更加轻松。

框架实现对开发流程的简化,其核心原理就是将控制权进行切换。 通过把原来由程序开发者掌握的程序控制权,移交给了框架,来消除开发者对功能调用的负担。 这个移交控制权的过程,就是控制扭转。

通过将控制权移交给框架之后,程序结构的齿轮就发生了变化。

3.png

在移交控制权后,控制程序走向的任务,就落到了框架身上。 由于框架的编写大多由对程序开发有强大理论储备和丰富经验的开发者来完成,所以我们极少需要关心他们操作那些工具齿轮的能力。

正是因为通过这些改变能够让我们减轻开发的负担,利用框架更容易的完成对业务处理的代码编写,所以目前绝大多数业务型框架,都采用了控制反转的设计思想。

依赖注入模式

我们知道,对象与对象存在的依赖关系,我们通常通过将被依赖的对象通过方法调用等手段。 而在实现控制反转的过程中,我们也会遇到对象依赖的问题,这就需要反转后的控制者,需要拥有对依赖管理的能力。

对于依赖处理,常见的有两种实现思路:

  • 依赖注入 ( Dependency Injection )
  • 依赖查找 ( Dependency Lookup )

在控制反转的程序结构中,我们最常用的就是依赖注入。 在控制反转中,程序控制者拥有者对依赖管理的能力,所以可以很方便的为对象准备所需的依赖,这就能够让我们省略对以来处理的代码。

4.png

依赖注入的方式有很多,如构造器注入、调用注入、参数注入等等。 在 Laravel 中,主要实现了构造器注入和调用注入。

Laravel 的服务容器

在依赖注入的过程中,依赖管理非常重要的一个环节,而容器 (Container) 正是为管理依赖而生。

容器也可以称为依赖注入容器 ( Dependency Injection Container ) ,容器能够完成对象实例化和依赖注入的过程。 而在这个过程中,被创建的对象本身,却并不知道容器的存在,也不知道自己正受到容器的管理。 这样的实现,就让控制反转后,程序与控制者的耦合性降到了最低。

正因为容器拥有着这样举足轻重的地位,所以目前各个语言最优秀的框架,几乎都离不开容器的支持。 例如 Java 领域的 Spring ,以及我们的主角 Laravel ,都是以容器为核心进行设计的。

在 Laravel 中,容器类==IlluminateContainerContainer== 实现构造器注入和调用注入两个方法分别为:

/**
 * 使用容器解析并返回对象
 */
protected function resolve($abstract, $parameters = [])
{
    // 当被解析的对象需要创建时,就会通过对象的构造方法进行依赖注入
}

/**
 * 使用容器调用方法
 */
protected function call($callback, array $parameters = [], $defaultMethod = null)
{
    // 对指定函数或方法的依赖参数进行注入,然后调用它并返回结果
}

在 Laravel 中,将不同的模块以服务 ( Service ) 的形式进行封装,所以 Laravel 的容器,就成为服务容器 ( Service Container ) 。

容器对依赖注入的实现

依赖注入的关键部分,就是对依赖的分析。 在 Laravel 的 IlluminateContainerContainer 中,通过 resolveDependencies 来分析和查找依赖参数。

/**
 * 分析依赖参数
 */
protected function resolveDependencies(array $dependencies)
{
    $results = [];

    foreach ($dependencies as $dependency) {
        // 如果存在传入参数覆盖,使用自定义的参数
        if ($this->hasParameterOverride($dependency)) {
            $results[] = $this->getParameterOverride($dependency);
            continue;
        }

        // 分析依赖参数的类型,根据不同的类型使用不同的参数解析方法
        $results[] = is_null($dependency->getClass())
                        ? $this->resolvePrimitive($dependency)
                        : $this->resolveClass($dependency);
    }

    return $results;
}

在 Laravel 中,会根据依赖参数的类型,选择获取依赖的方式。 其中,对于基础类型的参数,Laravel 会从传入参数和参数默认值中进行选择。 而对于需要的对象,Laravel 则会继续从容器中查找。 这样就形成了完整的通过容器,实现依赖处理逻辑。

小结
如果要解释我们为什么要使用依赖注入模式,那就是因为我们需要使用控制反转。 控制反转的程序设计思想,完全符合程序设计的依赖倒置原则,能够帮助我们实现的解耦。

Last modification:February 21st, 2018 at 02:07 pm

Leave a Comment