视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
控制反转原则,它和依赖注入有什么联系
2020-11-02 18:28:50 责编:小采
文档

控制反转(IOC)

首先,我们来看一个例子。

class Person
{
 private $name = '';
 private $age = 0;

 public function __construct(string $name, int $age)
 {
 $this->name = $name;
 $this->age = $age;
 }

 public function eat ()
 {
 echo '吃东西' . PHP_EOL;
 }

 public function drink ()
 {
 echo '喝水' . PHP_EOL;
 }

 public function sleep ()
 {
 echo '睡觉' . PHP_EOL;
 }

 public function wakeup ()
 {
 echo '起床' . PHP_EOL;
 }

 public function drive ()
 {
 echo '开车' . PHP_EOL;
 }

 public function wash ()
 {
 echo '洗漱' . PHP_EOL;
 }
}

小明现在早上起来需要去上班,那么小明需要做以下事情

$person = new Person('小明', 24);
$person->wakeup();
$person->wash();
$person->eat();
echo '带上车钥匙、手机、电脑' .PHP_EOL;
$person->drive();

上面的流程都是由程序员自己控制的。现在,我们想办法,让框架来控制流程。我们在Person类中新增一个方法,代码如下:

public function work (callable $bring)
{
 $this->wakeup();
 $this->wash();
 $this->eat();
 $bring();
 $this->drive();
}

小黄也需要去上班,现在他只安装框架的指导就可以完成上班的动作了。

$person = new Person('小黄', 29);
$person->work(function ()
{
 echo '带上手机、车钥匙、文件' . PHP_EOL;
});

修改后的代码完成了控制反转,以前的代码整个上班的流程由程序员控制,修改后的是由框架控制上班的流程的。程序的流程控制由程序员“反转”到了框架。

现在可以给出控制反转的定义了:

实际上,控制反转是一个比较笼统的设计思想,并不是一种具体的实现方法,一般用来指导框架层面的设计。这里所说的“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”给了框架。

依赖注入

控制反转是一种设计思想,而依赖注入是一种具体的编码技巧,依赖注入是实现控制反转最常用的技巧。依赖注入看起来“高大上”,实际上非常容易理解和掌握。

那到底什么是依赖注入呢?我们用一句话来概括就是:不通过 new() 的方式在类内部创建依赖类对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类使用。

下面来看一个实例:

interface Log
{
 function write (string $msg);
}

class TextLog implements Log
{
 public function __construct($dirname, $txtname)
 {
 $this->makeDir($dirname);
 $this->mkTxt($txtname);
 }

 private function makeDir (string $dirName) :void
 {
 // do something
 }

 private function mkTxt (string $txtName) :void
 {
 // do something
 }

 public function write (string $msg)
 {
 // do something
 }
}

class RedisLog implements Log
{
 private $redis = null;
 private $key = '';

 public function __construct(string $key)
 {
 $this->redis = '...'; // 获取redis实例
 $this->key = $key;
 // ...
 }

 public function write (string $msg)
 {
 // do something
 }
}

class App
{
 public function run ()
 {
 // do something

 // 记录日志
 (new RedisLog('log'))->write('框架运行信息记录');
 }
}

可以看到,App类依赖RedisLog类,如果我们今后不再使用redis来记录日子,而改用文本文件的话,那么就需要修改run里面的代码。

现在,我们换成使用依赖注入这种技巧来改写,代码如下;

class App
{
 private $logHandle = null;

 public function __construct(Log $log)
 {
 $this->logHandle = $log;
 }

 public function run ()
 {
 // do something

 // 记录日志
 $this->logHandle->write('框架运行信息记录');
 }
}

改写后的App类不再依赖RedisLog类,可以随时换成其他的Log类,只要该类实现了write方法即可。可以看到,使用了依赖注入,可以灵活的替换掉所依赖的类,另外它是编写可测试代码最有效的手段。

知乎里有一篇将依赖注入的文章,写的非常通俗易懂,大家也可以去看看。链接如下:

浅谈控制反转与依赖注入 https://zhuanlan.zhihu.com/p/33492169

下载本文
显示全文
专题