DI

  1. 依赖
    如果在 Class A 中,有 Class B 的实例,则称 Class A 对 Class B 有一个依赖
1public class A {
2    ...
3    B _b;
4    ...
5    public A() {
6        _b = new B();
7    }
8}

仔细看这段代码我们会发现存在一些问题:
(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 方式,如下:

1public class A{
2    ...
3    B _b;
4    ...
5    public A(B b) {
6        this._b = b;
7    }
8}

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

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

3. Java 中的依赖注入

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

1public class A{
2    ...
3    @Inject
4    B _b;
5    ...
6    public A() {
7    }
8}

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

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

3. PHP 中的依赖注入

依赖注入示例:

 1interface animal{
 2    function eat();
 3}
 4
 5class dog implements animal{
 6    function eat(){
 7        echo "dog to eat".PHP_EOL;
 8    }
 9}
10
11class cat implements animal{
12    function eat(){
13        echo "cat to eat".PHP_EOL;
14    }
15}
16
17class menu{
18    protected $obj = '';
19
20    function __construct(animal $a){
21        $this->obj = $a;
22    }
23    function forEat(){
24        $this->obj->eat();
25    }
26}
27
28$dog = new dog;
29$m = new menu($dog);
30$m->forEat();

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

PHP接口(interface)的特点

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

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

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

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

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

完善版:

 1interfaceanimal{
 2    function eat();
 3}
 4
 5class dog implements animal{
 6    function eat(){
 7        echo "dog to eat".PHP_EOL;
 8    }
 9}
10
11class cat implements animal{
12    function eat(){
13        echo "cat to eat".PHP_EOL;
14    }
15}
16
17class snake implements animal{
18    protected $name = '';
19    function __Construct($name){
20        $this->name = $name;
21    }
22
23    function eat(){
24        echo $this->name." snake to eat".PHP_EOL;
25    }
26}
27
28
29class menu{
30    protected $obj = '';
31
32    function __construct(animal $a){
33        $this->obj = $a;
34    }
35    function forEat(){
36        $this->obj->eat();
37    }
38}
39
40class Container {
41    protected $binds;
42    protected $instances;
43
44    public  function bind($abstract, $concrete){
45        // 判断是不是闭包化
46        if ($concrete instanceofClosure) { 
47            $this->binds[$abstract] = $concrete;
48        } else {
49            $this->instances[$abstract] = $concrete;// 正常实例化
50        }
51    }
52
53    public function make($abstract,$parameters=[]){
54        if (isset($this->instances[$abstract])) {
55            return $this->instances[$abstract];
56        }
57        array_unshift($parameters, $this);
58        return call_user_func_array($this->binds[$abstract], $parameters);
59    }
60}
61
62$obj = new Container;
63$dog = new dog();
64$obj->bind('Dog',$dog);
65$want_eat = $obj->make('Dog');
66$want_eat->eat();
67
68$snake = new snake('neinei');
69$obj->bind('Snake',$snake);
70$snake_eat = $obj->make('Snake');
71$snake_eat->eat();
72
73$obj->bind('Cat',function(){
74    return new cat;
75});
76$cat_eat = $obj->make('Cat');
77$cat_eat->eat();