Laravel底层原理(三) —— 依赖注入(DI)

Royal
2022-01-13 / 0 评论 / 441 阅读 / 正在检测是否收录...
  • 概念

    1. 依赖倒置原则 (Dependence Inversion Principle , 缩写为 DIP), 一种软件设计原则,是一个抽象的概念
    2. 控制反转 (Inversion of Control , 缩写为 IoC),DIP 的一种具体实现方式.
    3. 依赖注入 (Dependency injection , 缩写为 DI),IOC 的具体实现方式.

    依赖注入一词是由 Martin Fowler 提出的术语,它是将组件注入到应用程序中的一种行为。就像 Ward Cunningham 说的:

依赖注入是敏捷架构中关键元素。
  • 什么是依赖
    打个比方,就好比每个人都需要穿衣服才能进行正常的社会活动,这就是说是个人都需要依赖衣服才能正常社会活动。
    相应的,就是一个类需要另一个类才能完成工作,这就是依赖
    下面是person类依赖了衣服类

    class Person{
          protected $clothes;
          public function __construct() 
          {
              $this->clothes = new Clothes();
          }
    }

    看上面的代码就知道 人依赖了衣服,

什么是依赖注入呢,我们改造下上面的代码

class Person {
        protected $clothes;
        public function __construct(Clothes $clothes) {
            $this->clothes = $clothes;
        }
    }

    $person = new Person(new Clothes());

这里的Person 依赖注入了clothes类

  • 什么是控制反转(IOC)

    • 某人想开车,于是自己去造车,此时可以理解为控制正转,我想要车多大我就造多大,想要车是什么颜色就涂什么颜色.
    • 最后发现造车太难,于是选择去经销商买一辆车,可以理解为控制反转,经销商只提供蓝色的车,我就只能买蓝色的车,我被
      经销商控制了,我依赖经销商给我注入车

    结论就是,造车难,买车容易,在写程序的时候使用依赖注入可以更好的解耦

  • 先看自己造车代码

    <?php
    class Car
    {
        //汽车可以跑,定义一个方法 run()
        public function run(){
            return '滴滴滴,车开了';
        }
    }
    
    class Person
    {
        private $car = null;//保存某人的车,暂时还没车
        public function drive(){
            $this->car = new Car();//要开车先造车,造了一辆车保存在某人的 $car 里
            return $this->car->run();//调用车的 run() 方法
        }
    }
    
    $xiaoming = new Person();
    echo $xiaoming->drive();//输出 滴滴滴,车开了
  • 通过经销商依赖注入

    <?php
    class Car
    {
        //汽车可以跑,定义一个方法 run()
        public function run()
        {
            return '滴滴滴,车开了';
        }
    }
    
    class Person
    {
        private $car = null;//保存某人的车,暂时还没车
    
        //new 某人的时候,就给他注入一辆车,通过 $a 传入构造方法,并保存在 $car 里
        public function __construct($a)
        {
            $this->car = $a;
        }
    
        public function drive()
        {
            return $this->car->run();//调用车的 run() 方法
        }
    }
    $car  = new Car();//买一辆车
    $xiaoming = new Person($car);//new 小明的时候,把刚才买的车注入
    echo $xiaoming->drive();//输出 滴滴滴,车开了

    万一 new 小明的时候,给他买了一架飞机,并注入,小明不会开飞机怎么办,通过类型限定解决这个问题

    class Person
    {
       .
       .
       .
        public function __construct(Car $a)// <----这里做类型限定,保证不会注入飞机,也不会注入火箭
        {
            $this->car = $a;
        }
        .
        .
        .
  • IOC容器
    在上例中,我们是手动的去 new 一辆车并注入给某人,现在用一个容器统一的去管理这些需要注入的类。

    <?php
    class Car
    {
        //汽车可以跑,定义一个方法 run()
        public function run()
        {
            return '滴滴滴,车开了';
        }
    }
    
    class Person
    {
        private $car = null;//保存某人的车,暂时还没车
    
        //new 某人的时候,就给他注入一辆车,通过 $a 传入构造方法
        public function __construct(Car $a)// <----这里做了类型限定
        {
            $this->car = $a;
        }
    
        public function drive()
        {
            return $this->car->run();//调用车的 run() 方法
        }
    }
    
    //写一个简单的 IoC 容器类
    class Container{
        private static $objArr = [];//定义一个静态的空数组
    
        public static function set($flag, Callable $func){
            self::$objArr[$flag] = $func;//存入键值对,键是一个字符串,作为标识符,值是一个匿名函数
        }
    
        public static function get($flag){
            $tmp = self::$objArr[$flag];//取出标识符对应的匿名函数,用$tmp临时保存一下
            return $tmp();//在$tmp后名加上括号,表示执行这个函数,并返回
        }
    }
    
    //下面这条语句执行完毕后,会在 $objArr 里存入一个键值对,键是 car ,值是这个匿名函数,该匿名函数返回的是创建 Car 对象的语句
    Container::set('Car', function(){
        return new Car();
    });
    
    //下面这条语句执行完毕后,会在 $objArr 里存入一个键值对,键是 person ,值是这个匿名函数,该匿名函数返回的是创建 Person 对象的语句
    Container::set('Person', function(){
        return new Person(Container::get('Car'));//直接去容器中取一辆车出来,并作为参数传给 Person 类的构造函数
    });
    
    $xiaomin = Container::get('Person');//直接去容器中取一个人出来,取名叫小明
    echo $xiaomin->drive();//输出 滴滴滴,车开了

    IOC 是什么呢,看明白了依赖注入 (DI) 后就很容易理解了
    通过 DI 我们可以看到,一个类所需要的依赖类是由我们主动实例化后传入类中的。
    控制反转的意思就是说将依赖类的控制权交出去,由主动变被动。

  • 依赖注入的方式
    上面的例子都是通过构造方式的形式依赖注入进去,那还能通过其他方式么?
  • Setter 方法注入#

    class UserProvider{
        protected $connection;
        public function __construct(){
            ...
        }
    
        public function setConnection( Connection $con ){
            $this->connection = $con;
        }
        ...
  • 接口注入#

    interface ConnectionInjector{
        public function injectConnection( Connection $con );
    }
    
    class UserProvider implements ConnectionInjector{
        protected $connection;
    
        public function __construct(){
            ...
        }
    
        public function injectConnection( Connection $con ){
            $this->connection = $con;
        }
    }

    所以,简单讲只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于依赖注入(DI) 。是不是豁然开朗?事实上,就是这么简单。

0

评论

博主关闭了当前页面的评论