您当前的位置: 首页 > 慢生活 > 程序人生 网站首页程序人生
5-2 YII依赖注入之容器
发布时间:2023-04-29 21:10:37编辑:雪饮阅读()
-
容器container为耦合度紧密的程序提供一个行之有效的解决方案,称之为依赖注入。
第一步就是先将原来一个个new然后去调用的方式用container的get去实现
第二步就是说咱们依赖中如果还有依赖,但只要在单参构造类型有显式声明的情况下container仍然自动为你解决了深层次的依赖关系,相当于说是自动new。
第三步就是该考虑将实现类抽象为接口的实现方式,使得该方案更灵活(如果一个方案实现了80%的程度,突然某个依赖需要变更为类同的依赖类,难不成要重写。。。)
第四步就是在第三步的基础上,就是说有可能一个解决方案里面可能涉及多个接口(我要建立一个网站,我可能域名和服务器在不同的运营商购买的,类似这样,当然可能不是非常贴切),那么我们就了解到了第三步实现的另外一层好处---------可配置。
那么具体实现如:
<?php
namespace app\controllers;
use yii\di\Container;
use yii\web\Controller;
//step1
class ManDriver{
public function drive(){
echo 'i am an old man!';
}
}
class Car{
private $driver=null;
public function __construct(ManDriver $driver)
{
$this->driver=$driver;
}
public function run(){
$this->driver->drive();
}
}
//step2
Class Person{}
class ManDriver2{
private $person=null;
public function __construct(Person $person)
{
$this->person=$person;
}
public function drive(){
echo 'i am an old man!';
}
}
class Car2{
private $driver=null;
public function __construct(ManDriver2 $driver)
{
$this->driver=$driver;
}
public function run(){
$this->driver->drive();
}
}
//step3
interface Driver{
public function drive();
}
class ManDriver3 implements Driver {
public function drive(){
echo 'i am an old man!';
}
}
class Car3{
private $driver=null;
public function __construct(Driver $driver)
{
$this->driver=$driver;
}
public function run(){
$this->driver->drive();
}
}
//step4
interface Cup{
public function drink();
}
interface Driver4{
public function drive();
}
Class RedCup implements Cup{
public function drink(){
echo 'I am drinking water from a red cup.';
}
}
class ManDriver4 implements Driver4 {
public function __construct(Cup $cup)
{
$cup->drink();
echo "<hr/>";
}
public function drive(){
echo 'After drinking water, I will drive.';
}
}
class Car4{
private $driver=null;
public function __construct(Driver4 $driver)
{
$this->driver=$driver;
}
public function run(){
$this->driver->drive();
}
}
class HelloController extends Controller{
public function actionIndex(){
/*
* 依赖注入step1
* 对于上面ManDriver和Car两个类在Car类的run方法上是紧密耦合的,如果说
*要调用Car的run方法,则Car实例化的时候需要先实例化ManDriver做为唯一的构造参数
* 但是如果使用了Container容器技术,会自动识别构造函数中所定义的类就会自动帮你构造
* 像是这里就只需要通过Container的get方法指定Car的类的名称空间路径
* 这样实现看起来和原来相比好像是省代码量省的不多
* 但是根据我的理解,假如说上面ManDriver类的构造也又依赖另外一个类的实例呢?
* 一旦这种依赖比较多的时候,则下面的这种实现的优势就会出来了。
*
* */
$container=new Container;
$car=$container->get('app\controllers\Car');
$car->run();
}
public function actionIndex2(){
/*
* 依赖注入step2
* 当耦合关系比较深的时候的优势就出来了,这里按传统的方式则至少需要3次new才能正式调用
* */
$container=new Container;
$car=$container->get('app\controllers\Car2');
$car->run();
}
public function actionIndex3(){
/*依赖注入step3
* 虽然依赖注入step2解决了多次new的问题,但是可以发现还是有一个问题就是Car2强依赖于ManDriver2
* 假如又要来一个WomenDriver2,那么依赖注入step2的方式就不太灵活了
* 于是乎,这个应该将ManDriver2和WomenDriver2用interface与Car3的单参构造类型上
*但由于以interface实现,容器就没有办法直接new进行实例化了
* 因为接口是有很多的啊,具体是那个类来实现这个接口了,
* 所以就需要指定当前所用的接口,应该使用的实现类
* 这里就用到了container的set方法了
* set方法第一个参数就是当前container要进行运行的这个Car3类(可以理解为入口类)的单参构造的类型
* 或者说是接口的名称空间路径,那么第二个参数就是对应于这个单参构造类型的具体实现类的名称空间路径
* 如此一来就解决了强依赖问题,那么从set处就可以很灵活的更好要用的接口实现类
* 比如说有type-c接口以及传统的usb接口,假如说我一拖三的这种那我不同接口的手机
* 我只需要更换一拖三上面的不同线即可
* */
$container=new Container;
$container->set('app\controllers\Driver','app\controllers\ManDriver3');
$car=$container->get('app\controllers\Car3');
$car->run();
}
public function actionIndex4(){
/*
* 依赖注入step4
* 在依赖注入step3中可以看到通过接口实现了实现类的更换,解决了强依赖问题
* 但是从new不同类到现在这种通过container间接的去调用的转变,如果单纯从解耦合
* 方面考虑确实是不错的,但是仅仅只是这样吗?
* 答案是否定的,假如说你的业务中接口依赖比较多,然后这些接口之间又经常变更
* 业务需求总是多变的,已经不是一个非常意外的话题了。
* 难不成要频频修改实现的代码?对于php或许尚可,但php现在也开始流行编译方式运行了
* 所以频频修改代码当然不是最完美的
* 可以观察到现在我们的容器set接口与实现类的映射关系完全是字符串的参数
* 那么也就是说我们可以将其做为配置项,那么业务需求变更我们直接读取配置项里面的配置字符串
* 做为这里的参数岂不是很完美,配置项的改变总是简单的,比起代码的修改来说
* */
$container=new Container;
$container->set('app\controllers\Driver4','app\controllers\ManDriver4');
$container->set('app\controllers\Cup','app\controllers\RedCup');
$car=$container->get('app\controllers\Car4');
$car->run();
}
}
关键字词:依赖注入,容器
上一篇:4-3 YII行为之对象混合