目录

IOC、DI ,控制反转与依赖注入已经成为现代web框架的标配专业术语。很多初学者难以理解具体含义与方法,其实都是从我们编码过程抽象归纳总结而来,大部分人都有碰到甚至自己独立处理过,只是不知道设计方式还有这么个名字。

控制反转前的代码

先看一个例子:


class A
{
    protected $b;    
    
    public function method()
    {
        $this->b = new B();
        
        $this->b->method();        
         
        //TODO
    }
}
 
class B
{    
    public function method()
    {
        //TODO        
    }
}
 
//实例化A并执行A的method方法
$a = new A();
$a->method();

从上面的代码,可以看出类A依赖于类B。在没有考虑依赖注入方法前,我们大部分是这么写的代码。

如果之后要对类B类进行修改,可能会影响类A,也就是常说的耦合度高,我们想让类A和类B尽量的独立,使得耦合度低,不论是代码层面更清楚,开发过程中也适合多人协同而不互相影响,避免协同篡改他人代码。

控制反转设计原则

我们的目标就是在改动类B的情况下,尽量不影响类A。这里就涉及到了IOC控制反转设计原则。

高层模块不应该依赖于底层模块,两个都应该依赖抽象。

控制反转是一种设计原则,通过依赖注入DI方法实现这个原则。

构造器注入

初学者,通常会使用构造器注入的方式。

class A
{
    public $b;
    
    public function __construct($b)
    {
        $this->b=$b;        
    }
    public function method()
    {
        $this->b->method();
    }
}


//调用端: 

$a=new A(new B());
$a->method();

构造器注入的方式比没有采用依赖注入的方式会好点,至少依赖类B的实例化不再类A中,且类B外部调整或扩展,不会影响类A。

比如我们需要扩展类B,method方法逻辑要改变,我们并不是直接修改类B,而是实现一个类B1继承类B。


class B1 extends B
{
    
    public function method()
    {
        //TODO Other        
    }
}


//只要在调用端调整传入类A构造器的参数对象B1,不调整也不影响类A。

$a=new A(new B1());
$a->method();

也有一部分同学会使用普通方法注入的方式,比如setB()等,这些与构造器的注入大同小异。

容器注入

现在高大上的叫法叫容器注入,实际就是采用工厂模式注入,也是我们强烈推荐使用的,容器注入的方式可以达到高内聚、低耦合的理想目标。

我们实现一个容器类来管理类之间的依赖关系。当然所有类本身又依赖于容器类,所以容器类也需要注入业务类中,但容器类注入业务类,我们就采用简单的构造器或方法注入就可以了,因为只有一个依赖,且容器类简单不复杂。

<?php
class Container {
    private $s = array();
    //set可以简化成通过类名自动检测及自动实现类实例化或匿名函数
    public function set($k, $c) { 
        $this->s[$k]=$c; 
    }
    public function get($k) { 
        return $this->s[$k]($this); //这里始终注入容器类本身给业务类
    }
}

class A
{
    public $b;    
    public $constainer;
    public function __construct(Container $constainer)
    {
        $this->constainer = $constainer;
    }
    public function method()
    {        
        $this->b = $this->constainer->get("B");//从容器类中获取类B实例        
        $this->b->method();                 
        //TODO
    }
}

class B{
    public $constainer;
    public function __construct(Container $container){
        $this->constainer = $constainer;
    }
    public function method(){
        //同样在类B中,我们也可以通过constainer容器类来调用已经注册到容器中的相关依赖类
        //TODO
    }
}

$container = new Container();
$container->set("B",new B());//将类B注入容器
$container->set("B",function(){return new B();});//通过匿名函数将类B注入容器,可以保证传入时不会马上实例化,而是在真正调用时才完成实例化工作
$container->set("C",new C());//将类B注入容器

$a = new A($container);
$a->method();



本文收藏来自互联网,仅用于学习研究,著作权归原作者所有,如有侵权请联系删除

markdown @tsingchan

引用格式为收藏注解,比如本句就是注解,非作者原文。