DI
- 依赖
如果在 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 Guice、Spring 等等。
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();