DI

1. 依赖
如果在 Class A 中,有 Class B 的实例,则称 Class A 对 Class B 有一个依赖

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

仔细看这段代码我们会发现存在一些问题:
(1). 如果现在要改变 _b 生成方式,如需要用 new B(...) 初始化 _b,需要修改 A 代码;
(2). 如果想测试不同 B 对象对 A 的影响很困难,因为 _b 的初始化被写死在了 A的构造函数中;
(3). 如果new B()过程非常缓慢,单测时我们希望用已经初始化好的 _b 对象 Mock 掉这个过程也很困难。

2. 依赖注入

简单的来说,依赖注入就是不在A类中进行实例化B,而是在A类的外面就把B类实例化好,然后通过参数的方式传入A类,再在A类中赋值给A类里的自定义变量

上面将依赖在构造函数中直接初始化是一种 Hard init 方式,弊端在于两个类不够独立,不方便测试。我们还有另外一种 Init 方式,如下:

public class A{
    ...
    B _b;
    ...
    public A(B b) {
        this._b = b;
    }
}

上面代码中,我们将 B 对象作为构造函数的一个参数传入。在调用 A 的构造方法之前外部就已经初始化好了 B 对象。像这种非自己主动初始化依赖,而通过外部来传入依赖的方式,我们就称为依赖注入。

现在我们发现上面 1 中存在的两个问题都很好解决了,简单的说依赖注入主要有两个好处:
(1). 解耦,将依赖之间解耦。
(2). 因为已经解耦,所以方便做单元测试,尤其是 Mock 测试。

3. Java 中的依赖注入

依赖注入的实现有多种途径,而在 Java 中,使用注解是最常用的。通过在字段的声明前添加 @Inject 注解进行标记,来实现依赖对象的自动注入。

public class A{
    ...
    @Inject 
    B _b;
    ...
    public A() {
    }
}

上面这段代码看起来很神奇:只是增加了一个注解,B 对象就能自动注入了?这个注入过程是怎么完成的?

实质上,如果你只是写了一个 @Inject 注解,B 并不会被自动注入。你还需要使用一个依赖注入框架,并进行简单的配置,现在 Java 语言中较流行的依赖注入框架有 Google GuiceSpring 等等。

3. PHP 中的依赖注入

依赖注入示例:

interface animal{
    function eat();
}

class dog implements animal{
    function eat(){
        echo "dog to eat".PHP_EOL;
    }
}

class cat implements animal{
    function eat(){
        echo "cat to eat".PHP_EOL;
    }
}

class menu{
    protected $obj = '';

    function __construct(animal $a){
        $this->obj = $a;
    }
    function forEat(){
        $this->obj->eat();
    }
}

$dog = new dog;
$m = new menu($dog);
$m->forEat();

此例中用到了 PHP 的接口类,避免了不同依赖对象方法名不统一的问题。

PHP接口(interface)的特点

1、接口的方法必须是公开的。

2、接口的方法默认是抽象的,所以不在方法名前面加abstract。

3、接口可以定义常量,但不能定义成员属性,常量的定义和用法和类中常量一样。

4、类可以实现多个接口(相当于把多个功能集于一身,如手机实现了小灵通、MP3、MP4的功能)

5、接口也可以继承接口。

完善版:

interface animal{
    function eat();
}

class dog implements animal{
    function eat(){
        echo "dog to eat".PHP_EOL;
    }
}

class cat implements animal{
    function eat(){
        echo "cat to eat".PHP_EOL;
    }
}

class snake implements animal{
    protected $name = '';
    function __Construct($name){
        $this->name = $name;
    }

    function eat(){
        echo $this->name." snake to eat".PHP_EOL;
    }
}


class menu{
    protected $obj = '';

    function __construct(animal $a){
        $this->obj = $a;
    }
    function forEat(){
        $this->obj->eat();
    }
}

class Container {
    protected $binds;
    protected $instances;

    public  function bind($abstract, $concrete){
        if ($concrete instanceof Closure) { // 判断是不是闭包化
            $this->binds[$abstract] = $concrete;
        } else {
            $this->instances[$abstract] = $concrete;// 正常实例化
        }
    }

    public function make($abstract,$parameters=[]){
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }
        array_unshift($parameters, $this);
        return call_user_func_array($this->binds[$abstract], $parameters);
    }
}

$obj = new Container;
$dog = new dog();
$obj->bind('Dog',$dog);
$want_eat = $obj->make('Dog');
$want_eat->eat();

$snake = new snake('neinei');
$obj->bind('Snake',$snake);
$snake_eat = $obj->make('Snake');
$snake_eat->eat();

$obj->bind('Cat',function(){
    return new cat;
});
$cat_eat = $obj->make('Cat');
$cat_eat->eat();

Comments are closed.