目标
Yii1.x经常被批评为强耦合,很难使用第三方库。通常持这样的观点的人会提及Yii1对DI的支持不够,或者说几乎没有。其实个人认为还是有一些朴素的支持的,比如application就是一个DI,各种组件都是通过 Yii::app()->xxx 的方式创建并引用的,怎么能说没有DI呢?至少实现了一些基本要素:
- 依赖的创建交给了application,而不需要程序员手动在代码中 new来
- 维护了一个依赖列表,同一个依赖,在多次被使用的时候,只需要创建一次
作为富有进取心的框架,Yii2肯定不能再继续受这样的批评啦,于是在DI上做了很多工作,达到了DI的几个基本要求
- 创建过程自动化,也就是Ioc,控制反转,即不需要程序员手动new
- 依赖的依赖管理,即当依赖又依赖于其他依赖时,能够自动创建其他依赖
关于Yii2的依赖注入实现与原理,有网友已经给出了详细的分析,推荐大家看看。地址:http://www.digpage.com/di.html,写得相当好啦,很赞。
实际例子
既然依赖注入据说有利于降低各个模块之间的耦合度,那么就让我们试试效果。架设我们的Store模块需要一个AdminContext的依赖,那么我们可以在模块的init方法中将依赖加进去:
<!-- lang: php -->
//文件 StoreModule.php
public function init()
{
parent::init();
//开始添加依赖
$this->set('ac',[
'class'=>'mtBridge\services\AdminContext',
]);
}
上面的代码,因为Module本身是继承自ServiceLocator,所以可以直接使用set方法添加一个依赖项。再看看AdminContext类的定义:
<!-- lang: php -->
class AdminContext implements IAdminContext
{
public $env;
public $cs;
public function __construct(EnvContext $e,CacheService $c)
{
$this->env = $e;
$this->cs = $c;
}
public function doCallTest()
{
$siteName = $this->env->getSiteName();
$s = $this->cs->getCacheKey(1,'site');
return array($siteName,$s);
}
}
发现AdminContext的构造函数需要EnvContext和CacheService两个类型的对象作为参数,正好测试Yii2的DI的依赖的子依赖的支持。加个测试页面吧。
<!-- lang: php -->
class TestController extends \yii\web\Controller
{
public function actionIndex()
{
list($siteName,$key) = $this->module->get('ac')->doCallTest();
list($siteName2,$key2) = $this->module->get('ac')->doCallTest();
echo $siteName . '--key--' . $key;
}
}
运行这个action的代码,正常输出,并且两次调用,ac也就实例化一次。