php依赖注入与容器,Container,控制反转

依赖注入与Ioc容器

概念:

  1. 容器:可以理解为用来存放某个东西的物品(篮子?),存放的东西取决于你想往里面放点什么。在这里,我们是存放某个类,类的描述或者一个返回类实例的闭包函数。
  2. Ioc(Inversion of Control) 控制反转:可以理解为,你(用户),小红(容器)。你现在需要一把锤子,但你不想自己去造一个锤子去。你可以交给小红去处理。比如对小红说我想要一把锤子。小红会通过你给的工具名(锤子),通过自己的方法。去得到锤子的原材料(类的构造),接着在小红这里,直接造出来了一把锤子,不需要你来动手。你不需要知道这个锤子(类)所需的原材料(这里是指类的构造参数)。你通过小红(容器)而获取到了这个工具(类的实例).
  3. DI(Dependency Injection) 依赖注入:这和Ioc是同一种东西,但不同的是角度。例如:工具(锤子)依赖于小红(容器)去获取工具的原材料,并创建出工具(锤子).

特性:

  1. 减少系统之间的耦合性
  2. 增加代码稳定和健壮性
  3. 也可以理解为工厂模式的一种升级

php大神聚集地:294088839

Demo:

Class Demo1
{
    public $name;
    public function __construct(Demo2 $demo)
    {
        $this->demo = $demo;
    }
    public function Name()
   {
   $this->name = $this->demo->getName();
   return $this->name;
   }
}
//Demo2.php
Class Demo2
{
    public function getName()
    {
        return "名字是xxx<br>";
    }
}
//正常情况下,我们是需要先实例demo1然后在demo1的构造函数内传入demo2的
//实例,这样的耦合度太高,不宜于第二次扩展
//一般情况下的手法
//直接在new Demo1时就把Demo2给new出来并传入进去
$demo = new Demo1(new Demo2());
echo $demo->Name(); //输出名字是xxx

//通过Ioc容器实现
Class Container
{
    //存储当前类的实例
    private static $instance;
    //设置类不能直接new
    private function __construct(){}
    //禁止复制当前类
    private function __clone(){}
    //获取当前类的实例
    public static function _ins()
    {
        //判断成员变量是否存储实例
        if(empty(self::$instance))
        {
            //如果没有则存储并返回实例
            self::$instance = new static();
            return self::$instance;
        }
        //如果存储则直接返回
        return self::$instance;
    }
    //成员变量register存储类的实例或类的描述
    private $register  = [];
    //通过魔术方法__set和__get实现
    //设置未定义的成员变量时,会经过__set
    public function __set($key,$Cvalue)
    {
        //判断是否已经存储
        if(array_key_exists($key,$this->register))
        {
            throw new Exception("错误,已存在这一的一个类");
        }
        $this->register[$key] = $Cvalue;
    }
    //访问未定义的成员变量
    public function __get($key)
    {
        //通过build动态的去获取到类的实例
        return $this->build($this->register[$key]);
    }
    //自动绑定,自动解析
    public function build($ClassName)
    {
        //如果是匿名函数则直接返回执行后的结果
        if ($ClassName instanceof Closure)
        {
            return $ClassName($this);
        }
        //通过反射获取到类的内部结构
        $reflector  = new ReflectionClass($ClassName);
        //判断类能不能实例化,排除掉抽象类和接口
        if(!$reflector->isInstantiable())
        {
            throw new Exception("对象不能实例化");
        }
        //获取到类的构造函数参数
        $constructor = $reflector->getConstructor();
        //判断构造参数是否没有定义,如果没有,则直接返回类实例
        if(empty($constructor))
        {
            return new $ClassName();
        }
        //获取到构造函数内的参数
        $params = $constructor->getParameters();
        //递归的去调用方法解析并构造参数
        $dependencies = $this->getDependencies($params);
        //创建类的实例
        return $reflector->newInstanceArgs($dependencies);
    }
    //解析参数
    public function getDependencies($parameters)
    {
        //存储解析后的参数
        $dependencies = [];
        /** foreach循环获取参数,如果是变量并有默认值就直接返回默认值,如果没有 */
        foreach ($parameters as $parameter) {
            /** 通过反射获取到参数的类名,如果没有。。则直接返回默认值*/
            $dependency = $parameter->getClass();
            if (is_null($dependency)) {
                // 是变量,有默认值则设置默认值
                $dependencies[] = $this->resolveNonClass($parameter);
            } else {
                // 是一个类,递归解析
                $dependencies[] = $this->build($dependency->name);
            }
        }
        return $dependencies;
    }
    public function resolveNonClass($parameter)
    {
        // 有默认值则返回默认值
        if ($parameter->isDefaultValueAvailable()) {
            return $parameter->getDefaultValue();
        }
        //没有默认值就发出警告
        throw new Exception('参数没又默认值');
    }
}

//通过Ioc容器获取的
//实例化容器
$app = Container::_ins();
//直接依赖注入
$app->demo1 = 'Demo1';
$demo1 = $app->demo1;
//输出名字是xxx
echo $demo1->Name();

  

参考资料:

posted @ 2020-04-12 11:25  Death-Satan  阅读(661)  评论(0编辑  收藏  举报