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 Guice、Spring 等等。
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();