PHP当中的依赖注入和控制反转是面试中经常问到的问题。同时,很多框架中也使用到了这种设计模式,可能有很多小伙伴们对这两个概念认识比较模糊,下面我就来给大家讲讲什么是依赖注入。
依赖注入
概念:是一种允许我们从硬编码的依赖中解耦出来,从而在运行时或者编译时能够修改的软件设计模式。
说明:要想理解DI(依赖注入),首先要明白在PHP中是如何传递依赖关系的。
PHP中传递依赖关系的三种方式:
①在A类中直接new一个B类,代码如下:
<?php //声明一个类B class B{ //声明一个测试方法 public function test(){ //返回一个测试话术 return '这是一个测试方法'; } } //声明一个类A(假设A类想实现某个功能,要依赖B类来实现) class A{ //声明一个测试方法 public function toTest(){ //声明一个B类的对象 $bObj = new B(); //调用B类的方法 var_dump($bObj->test()); } } //创建一个A类的对象 $obj = new A(); //调用A类的测试方法 $obj->toTest();
说明:这是一种最不可取的方案,这样的话A和B就耦合在一起了,也就是A类无法脱离B类而工作。
②在A类的方法中传入需要的B类,代码如下:
<?php //声明一个类Bclass B{ //声明一个测试方法 public function test(){ //返回一个测试话术 return '这是一个测试方法'; }}//声明一个类A(假设A类想实现某个功能,要依赖B类来实现)class A{ //声明一个变量 public $b; //使用一个魔术方法 public function __construct(B $b) { $this->b = $b; } //声明一个测试方法 public function toTest(){ //调用B类的方法 var_dump($this->b->test()); }}//创建一个A类的对象$obj = new A(new B());//调用A类的测试方法$obj->toTest();
说明:这种方式比第一种方式改进了不少,A类不必和B类捆绑在一起,只要传入的类满足A类的需求,也可以是C类,也可以是D类等等。但是这种方案的弊端在于如果A类依赖的类较多,参数列表会很长,容易发生混乱。
③使用set方法传入,代码如下:
<?php //声明一个类Bclass B{ //声明一个测试方法 public function test(){ //返回一个测试话术 return '这是一个测试方法'; }}//声明一个类A(假设A类想实现某个功能,要依赖B类来实现)class A{ //声明一个变量 public $b; //声明一个测试方法 public function set(B $b){ //调用B类的方法 $this->b = $b; } public function go(){ var_dump($this->b->test()); }}//创建一个A类的对象$obj = new A();//调用A类的测试方法(这个地方可以使用单例模式)$obj->set(new B());//调用go方法$obj->go();
说明:这种方法比第一种方法有了改进,A类不必与B类捆绑在一起,只要传入的类满足A类的需求,也可以是C类,也可以是D类等等。但是这种方案的弊端在于如果A类依赖的类较多,参数列表会很长,容易发生混乱,和第二种方案存在同样的弊端。当依赖的类增多时,我们会需要很多的set方法。
这个时候,我们如果有一个专门的类(或者说一个容器)可以帮我们管理这些依赖关系就好了--依赖注入
下面就是一个依赖注入的例子,代码如下:
<?php //声明一个容器类,用来注入对象class Container{ //声明一个数组,用来存放对象 private $s = array(); //声明一个方法,用来实现对象的存放 function set($k, $c) { //将对象存放进数组 $this->s[$k] = $c; } //声明一个方法,用来获取对象 function get($k) { //返回对应的对象 return $this->s[$k]; }}//声明一个Db类class Db{ //声明一个方法 public function doSomething(){ //返回话术 return '这是一个依赖注入的测试'; }}//声明一个Cache类class Cache{ //声明一个容器变量 private $container; //使用构造函数给container赋值 public function __construct(Container $container) { //将容器对象赋值给变量 $this->container = $container; } //声明一个方法 public function doSomething($str){ //执行Db类中对应的方法 var_dump($this->container->get($str)->doSomething()); }}//创建一个Container对象$container = new Container();//在容器中注入两个类$container->set('db', new Db());$container->set('cache', new Cache($container));//执行对应的方法$container->get('cache')->doSomething('db');