一、相关概念
依赖倒置(DIP)
DIP是一种设计思想,在传统的软件设计中,上层代码依赖下层,当下层代码发生改变的时候,上层代码也要发生改变,代码不易维护,而DIP设计思想要求定义上层接口,下层实现这个接口,从而降低耦合度,避免对原代码的侵入。
控制反转(IOC)
IOC是DIP的一种具体思路,将上层所依赖的下层交由第三方实现。也就是说,控制权转移,应用程序本身不负责依赖对象的创建和维护,而是有外部容器复制创建和维护。
依赖注入(DI)
DI是IOC的一种解耦组件之间依赖关系的设计模式,将类的依赖用另一个类来实例化,从而移到类的外部实现。
二、背景
说到换框架,有松耦合这些机制就尤为重要了。使用依赖注入(DI)就是实现松耦合的一个好方法,如果再配合上科学合理的架构设计,核心业务层可以几乎原封不动地移植到另一个使用DI的框架。
于是就有了对CI3引入一套流弊的依赖注入机制的想法,推荐?PHP-DI?相当成熟,功能强大。当初想,在不改动框架核心文件和不改变框架原有功能的前提下,把PHP-DI整合进来了,让在CI3.x上构建的项目可以使用依赖注入。此方案是基于不改CI框架核心代码的出发点非常不错的,不足的是实践中有一些问题,不得不放弃这种方案。有兴趣的同学可以参考这篇博文:https://zhuanlan.zhihu.com/p/98644060?
不推荐此方案原因
1. 本方案控制器实例会被初始化两次,多了一层资源的初始化,导致初始化方法里的代码多执行了一次,导致了又一次的无用计算资源开销。主要是初始化方法里,有许多校验逻辑。(主要原因)
2. 初始化方法中有许多全局常量定义,第二次初始化执行到他会报错
3. 第二次初始时,CI框架自动加载的自定义类库,会提示找不到文件
三、接入目的
PHP-DI提供了属性注入的方式,只要在容器配置文件里定义好就行,CI自动把控制器所需要的实例自动注册到控制器的属性上了。
四、CI框架集成
这里只针对CI框架,其他框架请参考?PHP-DI 官网?https://php-di.org/doc/frameworks/symfony2.html
composer require php-di/php-di
注:PHP-DI 需要 PHP 7.2 或更高版本
2. 接管控制器实例生成
核心方法,让DI容器生成控制器实例,而不是由框架直接new。修改 system/core/CodeIgniter.php 中的生成控制器实例的代码。
$reg = 'cdi';
$reg_file = APPPATH . 'config/'.$reg.'.php';
$builder = new DI\ContainerBuilder;
if(file_exists($reg_file)){
$builder->addDefinitions($reg_file);
}
$CI = $builder->build()->get($class);
//$CI = new $class(); // 注释CI本身的new方法,修改成DI容器生成控制器实例,也就是上边的代码
3. 依赖注册文件配置?
在application/config路径下安装DI容器配置文件cdi.php,这里以Test控制器类注入为例
use function DI\autowire;
use function DI\factory;
use function DI\create;
use function DI\get;
use App\models\ArticleRepository;
use App\libraries\CI_Instance as CI;
use App\third_party\Twig;
return [
'Test' => autowire()
->property('user_service', get(UserService::class))
];
4. 使用
做如上简单的改造后,你就可以使用依赖注入了,这里我们用属性注入举例,其他更多方法参PHP-DI考官网的文档。
/**
* 属性注入
* @var services\queue\UserService
*/
private $user_service;
public function i(){
// 调用
$this->user_service->test();
}
五、总结
此方案,虽说我们改动了框架的核心源码,在鱼和熊掌不可兼得情况下,我们必须做出取舍,才能充分压榨出计算机的更多性能来。关于更多的依赖注入使用方法和最佳事件,请移步 PHP-DI 官网 https://php-di.org?,去充分挖掘最佳实战技巧。
|