公告:
  • 要报名培训 PHP 的同学抓紧时间了,黎老师手把手教学,现在报名白菜价了 [2017-08-25]

依赖注入与控制反转

程序开发是吃青春饭的,青春年少时可以敲敲代码开发开发程序,到中老年还想在程序界混,那么就要掌握一点架构能力了,争取做个架构师,做个技术主管或技术顾问,要么自己当老板。那么想提高程序架构的能力就要学学下面的一些知识点了。

依赖注入与控制反转这两个名词在程序生涯中或多或少总能听到,以下我们分章节分点来讨论一下。

程序系统

程序系统是指一个高度封装的、能对外提一定供服务的接口集合。

接口

接口是一个广泛的名词,一个函数是一个接口,一个对象的方法也是一个接口,一个 url 地址也是一个接口。接口英文叫 "API"。

// 这是一个接口
function substr()
{

}

class Person
{
    // 这是一个接口
    public function getName()
    {
        return '张三';
    }
}

// 这也是一个接口
/*
    url: http://www.baidu.com
    请求方法: GET
    请求参数: string s
    返回值: string 返回 html 文档
*/

所以接口是一个广泛的概念,通过接口,就可以和系统交互。例如 windows 系统,对外暴露的很多接口,比如很多软件都可以复制文件,创建文件,删除文件,这些都直接或间接调用了 windows 的接口。

客户端

客户端又是一个广泛的概念,一个控制台是一个客户端,一个手机 QQ 是一个客户端,一个控制器方法是一个客户端,一个浏览器是一个客户端,等等。

在 PHP 的程序开发中,入口脚本和控制器方法这两个是典型的客户端,应该只在这些地方调用系统接口来实现业务逻辑。

依赖

一个系统的运行,需要另一个系统的辅助,这时我们就说这个系统依赖另一个系统。

在程序中,一个接口中直接调用了另一个接口,那么这个接口就依赖了另一个接口,没有那个接口,这个程序就不能运行。

反转

反转就是指一个接口依赖另一个接口的时候,不直接在这个接口内部调用依赖接口,而是让调用者把依赖的接口传递进去,这样就达到了反转的效果。由原来的直接在内部调用,变成了传递进去再使用,这就是控制反转。

class Baz
{
    public function getSomething()
    {
        return $this->something;
    }
}

class Foo
{
    public function bar(Baz $baz)
    {
        return $baz->getSomething();
    }
}

比如上面这段程序片段,Foo::bar 接口依赖了 Baz::getSomething 接口,但是并不是直接在 Foo::bar 接口的内部实例化 Baz ,而是让调用者传递一个 Baz 实例进来,这样做的好处是在传递 Baz 实例之前,还可以有机会配置一下 Baz 的实例,不然被你在内部直接实例化,封死在你的接口里面了,这样调用者就不能配置了。还有当你发布程序的时候最好就把依赖一起发布,不然调用者根本不知道你的接口内部依赖了谁,自然也就不能给你解决依赖。

比如下面这样,后者就强依赖前者:

class Baz
{
    public function getSomething()
    {
        return $this->something;
    }
}

class Foo
{
    public function bar()
    {
        return (new Baz())->getSomething();
    }
}

依赖注入容器

依赖多了,光光解决依赖就显得很麻烦,比如像下面这样,依赖注入容器可以很好地解决这个问题。依赖注入容器可以很智能地帮我们传递依赖。

我们知道,实例化一个类的时候,会首先调用类的构造方法,我们可以通过反射,分析构造方法,逐一满足构造方法的参数,如果依赖的是一个类,就先实例化这个类,它又依赖另一个,就又实例化另一个,一直有依赖就一直递归下去,直到解决完所有的依赖,碰到的是标量类型参数,就要求调用者事先传递了这个标量参数,容器早早已经存起来了这些标量参数。


// 省略很多

$a = new A();
$b = new B($a);
$c = new C($b);

下面我们用 PHP 语言一起实现一个依赖注入容器。需要读者掌握 php 语言,还要知道反射的使用。

依赖注入容器接口:

<?php

namespace allowing\core;

interface DiInterface
{
    public function set();

    public function get();
}

依赖注入容器实现:

<?php

namespace allowing\core;

class Di implements DiInterface
{
    public function set()
    {

    }

    public function get()
    {

    }
}

由于 PHP 语言的问题,方法的约束比较弱,所以要大量的注释来传达接口设计者的意图。如果读注释还不明白,那么就要问清楚接口设计者了,不然实现出来的未必是接口设计者想要的。

使用依赖注入容器:


好了,至此已经全部论述完了依赖注入和控制反转。

发表评论:(支持 Markdown 语法)
访客评论(0):