https://blog.csdn.net/zyddj123/article/details/82753650

什么是依赖注入?
IOC:英文全称:Inversion of Control,中文名称:控制反转,它还有个名字叫依赖注入(Dependency Injection,简称DI)。

当一个类的实例需要另一个类的实例协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。而采用依赖注入的方式,创建被调用者的工作不再由调用者来完成,因此叫控制反转,创建被调用者的实例的工作由IOC容器来完成,然后注入调用者,因此也称为依赖注入。

举个简单的例子:

(1)原始社会里,几乎没有社会分工。需要斧子的人(调用者)只能自己去磨一把斧子(被调用者)。

(2)进入工业社会,工厂出现。斧子不再由普通人完成,而在工厂里被生产出来,此时需要斧子的人(调用者)找到工厂,购买斧子,无须关心斧子的制造过程。

(3)进入“按需分配”社会,需要斧子的人不需要找到工厂,坐在家里发出一个简单指令:需要斧子。斧子就自然出现在他面前。

第一种情况下,实例的调用者创建被调用的实例,必然要求被调用的类出现在调用者的代码里。无法实现二者之间的松耦合。

第二种情况下,调用者无须关心被调用者具体实现过程,只需要找到符合某种标准(接口)的实例,即可使用。此时调用的代码面向接口编程,可以让调用者和被调用者解耦,这也是工厂模式大量使用的原因。但调用者需要自己定位工厂,调用者与特定工厂耦合在一起。

第三种情况下,调用者无须自己定位工厂,程序运行到需要被调用者时,依赖注入容器自动提供被调用者实例。事实上,调用者和被调用者都处于依赖注入容器的管理下,二者之间的依赖关系由依赖注入容器提供。因此调用者与被调用者的耦合度进一步降低,这使得应用更加容易维护,这就是依赖注入所要达到的目的。

用php实现一个轻量的依赖注入容器
首先我们创建一个类,看起来是这样的:

<?php
class Di
{
protected $_service = [];
public function set($name, $definition)
{
$this->_service[$name] = $definition;
}
public function get($name)
{
if (isset($this->_service[$name])) {
$definition = $this->service[$name];
} else {
throw new Exception("Service '" . name . "' wasn't found in the dependency injection container");
}

if (is_object($definition)) {
$instance = call_user_func($definition);
}

return $instance;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
现在我们已经有了一个简单的类,包含一个属性和两个方法。假设我们现在有两个类,redisDB和cache,redisDB提供一个redis数据库的操作,cache负责缓存功能的实现并且依赖于redisDB。

class redisDB
{
protected $_di;

protected $_options;

public function __construct($options = null)
{
$this->_options = $options;
}

public function setDI($di)
{
$this->_di = $di;
}

public function find($key, $lifetime)
{
// code
}

public function save($key, $value, $lifetime)
{
// code
}

public function delete($key)
{
// code
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
在这个类中我们简单实现了redis的查询、保存和删除。你可能会有疑问,另外一个方法setDi是做什么的。待我继续为你讲解。另一个类和当前这个类结构很像:

class cache
{
protected $_di;

protected $_options;

protected $_connect;

public function __construct($options = null)
{
$this->_options = $options;
}

public function setDI($di)
{
$this->_di = $di;
}

protected function _connect()
{
$options = $this->_options;
if (isset($options['connect'])) {
$service = $options['connect'];
} else {
$service = 'redis';
}

return $this->_di->get($service);
}

public function get($key, $lifetime)
{
$connect = $this->_connect;
if (!is_object($connect)) {
$connect = $this->_connect()
$this->_connect = $connect;
}
// code
...
return $connect->find($key, $lifetime);
}

public function save($key, $value, $lifetime)
{
$connect = $this->_connect;
if (!is_object($connect)) {
$connect = $this->_connect()
$this->_connect = $connect;
}
// code
...
return $connect->save($key, $lifetime);
}

public function delete($key)
{
$connect = $this->_connect;
if (!is_object($connect)) {
$connect = $this->_connect()
$this->_connect = $connect;
}
// code
...
$connect->delete($key, $lifetime);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
现在我们就当已经实现了redisDB和cache这两个组件,具体的细节这里就先不做讨论了,来看看如何使用使用吧。首先需要将两个组件注入到容器中:

<?php
$di = new Di();
$di->set('redis', function() {
return new redisDB([
'host' => '127.0.0.1',
'port' => 6379
]);
});
$di->set('cache', function() use ($di) {
$cache = new cache([
'connect' => 'redis'
]);
$cache->setDi($di);
return $cache;
});

// 然后在任何你想使用cache的地方
$cache = $di->get('cache');
$cache->get('key'); // 获取缓存数据
$cache->save('key', 'value', 'lifetime'); // 保存数据
$cache->delete('key'); // 删除数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
到这里你可能会觉得这样以来反而有点繁琐了。cache和redisDB的结构如此之像,完全可以把redis写到cache中而没必要单独分离出来?但是你想过没有,有些数据及时性没那么高而且数量比较大,用redis有点不合适,mongodb是更好的选择;有些数据更新频率更慢,对查询速度也没要求,直接写入文件保存到硬盘可能更为合适;再或者,你的客户觉得redis运维难度有点大,让你给他换成memcache… 这就是为什么把它分离出来了。然后,继续改进代码:

interface BackendInterface {
public function find($key, $lifetime);
public function save($key, $value, $lifetime);
public function delete($key);
}

class redisDB implements BackendInterface
{
public function find($key, $lifetime) { }
public function save($key, $value, $lifetime) { }
public function delete($key) { }
}

class mongoDB implements BackendInterface
{
public function find($key, $lifetime) { }
public function save($key, $value, $lifetime) { }
public function delete($key) { }
}

class file implements BackendInterface
{
public function find($key, $lifetime) { }
public function save($key, $value, $lifetime) { }
public function delete($key) { }
}

$di = new Di();
// redis
$di->set('redis', function() {
return new redisDB([
'host' => '127.0.0.1',
'port' => 6379
]);
});
// mongodb
$di->set('mongo', function() {
return new mongoDB([
'host' => '127.0.0.1',
'port' => 12707
]);
});
// file
$di->set('file', function() {
return new file([
'path' => 'path'
]);
});
// save at redis
$di->set('fastCache', function() use ($di) {
$cache = new cache([
'connect' => 'redis'
]);
$cache->setDi($di);
return $cache;
});
// save at mongodb
$di->set('cache', function() use ($di) {
$cache = new cache([
'connect' => 'mongo'
]);
$cache->setDi($di);
return $cache;
});
// save at file
$di->set('slowCache', function() use ($di) {
$cache = new cache([
'connect' => 'file'
]);
$cache->setDi($di);
return $cache;
});

// 然后在任何你想使用cache的地方
$cache = $di->get('cache');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
我们新增加了一个接口BackendInterface,规定了redisDB,mongoDB,file这三个类必须实现这个接口所要求的功能,至于其他锦上添花的功能,随你怎么发挥。而cache的代码,好像没有变,因为cache不需要关心数据是怎么存入数据库或者文件中。而cache的调用者,也不需要关心cache具体是怎么实现的,只要根据接口实现相应的方法就行了。多人协作你会更加受益,你们只需要商定好接口,然后分别实现就行了。

这就是依赖注入的魅力所在了,虽然看似如此简单。

以上代码还可以继续改进,直到你认为无可挑剔为止。比如,redis服务在一个请求中可能会调用多次,而每次调用都会重新创建,这将有损性能。只需扩展一下DI容器就好增加一个参数或增加一个方法,随你。

class Di
{
protected $_service = [];
protected $_sharedService = [];
public function set($name, $definition, $shared = false)
{
if ($shared) {
$this->_sharedService[$name] = $definition;
} else {
$this->_service[$name] = $definition;
}
}
public function get($name) {
if (isset($this->_service[$name])) {
$definition = $this->service[$name];
} else if ($this->_sharedService[$name]) {
$definition = $this->_sharedService[$name];
} else {
throw new Exception("Service '" . name . "' wasn't found in the dependency injection container");
}
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
这样以来,如果某个服务在一次请求中要调用多次,你就可以将shared属性设置为true,以减少不必要的浪费。如果你觉得每次在注入时都要setDi有点繁琐,想让他自动setDi,那可以这么做:

interface DiAwareInterface
{
public function setDI($di);
public function getDI();
}

class Di
{
protected $service;

public function set($name, $definition)
{
$this->service[$name] = $definition;
}

public function get($name)
{
...
if (is_object($definition)) {
$instance = call_user_func($definition);
}

// 如果实现了DiAwareInterface这个接口,自动注入
if (is_object($instance)) {
if ($instance instanceof DiAwareInterface) {
$instance->setDI($this);
}
}

return $instance;
}
}

class redisDB implements BackendInterface, DiAwareInterface
{
public function find($key, $lifetime) { }
public function save($key, $value, $lifetime) { }
public function delete($key) { }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
然后,就可以这样:

$di->set('cache', function() {
return new cache([
'connect' => 'mongo'
]);
});
1
2
3
4
5
我们现在所实现的这个DI容器还很简陋,还不支持复杂的注入,你可以继续完善它。

不过,通过这些代码你已经了解什么是依赖在注入了,你可以将这种思想应用到你的项目中,或者着手开发你自己的框架。如果想继续深入学习的话,烦请 click Scene。
---------------------
作者:李景山-编程者
来源:CSDN
原文:https://blog.csdn.net/lijingshan34/article/details/71526118
版权声明:本文为博主原创文章,转载请附上博文链接!

php 依赖注入 和 控制反转 php设计模式的更多相关文章

  1. 简单解析依赖注入(控制反转)在Spring中的应用

    IoC——Inversion of Control  控制反转DI——Dependency Injection   依赖注入 大家都知道,依赖注入是Spring中非常重要的一种设计模式.可能很多初学者 ...

  2. Java的依赖注入(控制反转)

    两个主角"依赖注入"和"控制反转": 1.二都说的都是同一件事,只是叫法不同.是一个重要的面向对象编程的法则,也是一种设计模式: 2.英文原称:依赖注入,Dep ...

  3. .NET Core的依赖注入[1]: 控制反转

    写在前面:我之前写过一系列关于.NET Core依赖注入的文章,由于.NET Core依赖注入框架的实现原理发生了很大的改变,加上我对包括IoC和DI这些理论层面的东西又有了一些新的理解,所以我在此基 ...

  4. 轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI) 依赖注入和控制反转的理解,写的太好了。

    轻松学,浅析依赖倒置(DIP).控制反转(IOC)和依赖注入(DI) 2017年07月13日 22:04:39 frank909 阅读数:14269更多 所属专栏: Java 反射基础知识与实战   ...

  5. 浅谈(IOC)依赖注入与控制反转(DI)

    前言:参考了百度文献和https://www.cnblogs.com/liuqifeng/p/11077592.html以及http://www.cnblogs.com/leoo2sk/archive ...

  6. Spring Framework------>version4.3.5.RELAESE----->Reference Documentation学习心得----->Spring Framework的依赖注入和控制反转

    Dependency Injection and Inversion of Control 1.概述: 1.1相关概念 bean:由IoC容器所管理的对象,也即各个类实例化所得对象都叫做bean 控制 ...

  7. 谈谈php依赖注入和控制反转

    要想理解php依赖注入和控制反转两个概念,就必须搞清楚如下的问题: DI--Dependency Injection   依赖注入 IoC--Inversion of Control  控制反转 1. ...

  8. 【AutoFac】依赖注入和控制反转的使用

    在开始之前首先解释一下我认为的依赖注入和控制反转的意思.(新手理解,哪里说得不正确还请指正和见谅) 控制反转:我们向IOC容器发出获取一个对象实例的一个请求,IOC容器便把这个对象实例“注入”到我们的 ...

  9. PHP 依赖注入和控制反转再谈(二)

    今天有个朋友看到yii2中介绍的依赖注入一头雾水,之前我写过类似的文章发给他看了,可能还没深入理解吧,这里我再通俗点描述下依赖注入的原理吧,尽可能滴说通俗易懂一点吧:先还是扯下概念性滴问题(概念问题我 ...

随机推荐

  1. C++用libcurl通过HTTP以表单的方式Post数据到服务器

    POST字符串 #include <stdio.h> #include <curl/curl.h> int main(void) { CURL* curl = NULL; CU ...

  2. 87.QuerySet API使用详解:create方法

    create:创建一条数据,并且保存到数据库中,这个方法相当于先用指定的模型创建一个一个对象,然后再调用这个对象的save方法,示例代码如下: from django.db import connec ...

  3. NFS 文件共享

    备注:NFS 文件共享需设置两部分:服务端和客户端 一.服务端设置 1.1.查看nfs包是否安装,未安装则重新安装 [root@localhost ~]# rpm -qa|grep rpcbind r ...

  4. 负载均衡配置篇(Nginx)

    负载均衡 == 分身的能力. 既然要有分身的能力嘛,这好办,多弄几台服务器就搞定了.今天我们讲的实例嘛…..我们还是先看图比较好: 还是图比较清晰,以下我都用别名称呼: PA : 负载均衡服务器/WE ...

  5. java使用forEach填充字典值

    // 填充字典值 Vector vector = vectorMapper.selectByPrimaryKey(id); VectorModel vectorModel = new VectorMo ...

  6. c#学习笔记02——接口

    本身并不实现功能,但提供一种模板定义,为从它继承类或结构提供了一种定义的规范 有了接口,程序员可以把程序定义的更积极啊清晰和条理化 理解接口 接口支持多继承:抽象类不能实现多继承 接口只能定义抽象规则 ...

  7. Linux-编写简单守护进程

    1.任何一个进程都可以将自己实现成一个守护进程 2.create_daemon函数要素 (1).子进程要等待父进程退出 (2).子进程使用setsid创建新的会话期,脱离控制台 (3).调用chdir ...

  8. Python(os和sys)使用

    Python(os和sys)理解 os模块负责程序与操作系统的交互,提供了访问操作系统底层的接口; sys模块负责程序与python解释器的交互,提供了一系列的函数和变量,用于操控python的运行时 ...

  9. 如何保存Keras模型

    我们不推荐使用pickle或cPickle来保存Keras模型 你可以使用model.save(filepath)将Keras模型和权重保存在一个HDF5文件中,该文件将包含: 模型的结构,以便重构该 ...

  10. 关于汽车诊断OBD的理解(ISO15031-5)(转发)

    1.OBD用来做什么 2.OBD和UDS的区别 3.OBD硬件接口简介 4.OBD的9大模式介绍 OBD(On-Board Diagnostic)指的是在线诊断系统,是汽车上的一种用于监控车辆状况以及 ...