zoukankan      html  css  js  c++  java
  • Laravel 服务容器,IoC,DI

    DI

    DI 就是常说的依赖注入,那么究竟什么是依赖注入呢?

    打个比方,电脑(非笔记本哈)需要键盘和鼠标我们才能进行操作,这个‘需要’换句话说就是‘依赖’键盘和鼠标。

    那么,相应的,一个类需要另一个类才能进行作业,那么这也就是依赖。

    看一段代码:

    class Computer {
            protected $keyboard;
    
            public function __construct() {
                $this->$keyboard = new Keyboard();
            }
        }
     // 这里的Computer类依赖了键盘类。

    好,既然我们已经知道了什么是依赖,那么什么是注入呢?

    我们改造一下上面的代码:

    class Computer {
            protected $keyboard;
    
            public function __construct(Keyboard $keyboard) {
                $this->$keyboard = $keyboard;
            }
        }
    
        $computer = new Computer(new Keyboard());
    
        //这里的Computer类依赖注入了Keyboard类。

    其实依赖注入就是:所需要的类通过参数的形式传入的就是依赖注入。

    理解了依赖注入,我们可以接着理解 IOC。

    IOC

    IOC 是什么呢?

    中文叫控制反转。啥意思呢? 这个看明白了 DI 后就能很容易的理解了。

    通过 DI 我们可以看到,一个类所需要的依赖类是由我们主动实例化后传入类中的。

    控制反转和这个有什么关系呢?控制反转意思是说将依赖类的控制权交出去,由主动变为被动。

    看一段 laravel 代码:

    namespace AppHttpControllers;
    
    use IlluminateHttpRequest;
    
    class SessionController extends Controller
    {
    
        public function login(Request $request)
        {
            //这就是IOC,我们不需要主动传入类了一切由laravel去实现
        }
    }

    看到这你可能有疑问了,这是怎么实现的呢?

    这就是靠服务容器了,请往下接着看。

    服务容器

    服务容器其实就是一种设计模式,它的目的就是解耦依赖。

    这里我们再从头至尾的看一遍,怎么一步步演化出服务容器。

    依然是电脑的例子,我们知道电脑依赖键盘鼠标,可是键盘鼠标也有很多种呀。

    先看一个最原始的代码例子:

    class Computer {
            protected $keyboard;
    
            public function __construct($type = null) {
    
                switch($type) {
                    case 'common':
                        return new CommonKeyboard();
                    case 'awesome':
                        return new AweSomeKeyboard();
                    default:
                        return new Keyboard();
                }
    
            }
        }

    或许你一眼就看出了问题在哪。

    如果我们又要增加一钟键盘,那我们又得对这个类进行修改。这样下去,这个类会变得庞大且耦合程度过高。

    那么我们可以怎么修改呢?

    • 工厂模式

    这样我们可以避免直接的修改 Computer 类。

    class Factory {
    
            public static function getInstance($type){
                switch($type) {
                    case 'common':
                        $this->keyboard = new CommonKeyboard();
                        break;
                    case 'awesome':
                        $this->keyboard = new AweSomeKeyboard();
                        break;
                    default:
                        $this->keyboard = new Keyboard();
                        break;
                }
            }
        }
    
        class Computer {
            protected $keyboard;
    
            public function __construct($type == null) {
                $this->keyboard = Factory::getInstance($type);
            }
        }

    这样使用简单工厂模式后,我们后续的修改可以不用对 Computer 类进行操作而只要修改工厂类就行了。这就相当于对 Computer 类进行了解耦。

    Computer 类虽不在依赖那些键盘类了,但是却变为依赖工厂类了。

    后续添加新类型的键盘就必须对工厂类进行修改。

    所以这个工厂类还不能很好的满足要求,我们知道电脑对键盘的接口都是一致的,键盘必须实现这一接口才能被电脑识别,那我们对 Computer 和 Keyboard 类进行修改。

    • DI(依赖注入)
     interface Board {
            public function type();
        }
    
        class CommonBoard implements Board {
            public function type(){
                echo '普通键盘';
            }
        }
    
        class MechanicalKeyboard implements Board {
            public function type(){
                echo '机械键盘';
            }
        }
    
        class Computer {
            protected $keyboard;
    
            public function __construct (Board $keyboard) {
                $this->keyboard = $keyboard;
            }
        }
    
        $computer = new Computer(new MechanialKeyBoard());

    可是这样也有问题,如果我们后续对这台电脑使用的键盘不满意要进行替换呢? 我们又回到原点了,必须去修改传入的键盘类。

    能不能做成可配置的呢?

    • IOC 服务容器(超级工厂)
    class Container
    {
        protected $binds;
    
        protected $instances;
    
        public function bind($abstract, $concrete)
        {
            if ($concrete instanceof Closure) {
                $this->binds[$abstract] = $concrete;
            } else {
                $this->instances[$abstract] = $concrete;
            }
        }
    
        public function make($abstract, $parameters = [])
        {
            if (isset($this->instances[$abstract])) {
                return $this->instances[$abstract];
            }
    
            array_unshift($parameters, $this);
    
            return call_user_func_array($this->binds[$abstract], $parameters);
        }
    }

    这就是一个简单的 IOC 服务容器。

    这个怎么解决我们上述的问题呢?

     $container = new Container;
    
        $container->bind('Board', function($container){
            return new CommonBoard;
        });
    
        $container->bind('Computer',function($container,$module){
            return new Computer($container->make($module));
        });
    
        $computer = $container->make('Computer',['Board']);

    这里生产出来的 Computer 类就是一个使用普通键盘的电脑类了。

    解释一下代码:

    bind(name,function($container){
          return new Name;
     })
    
    //这里的name和Name之间的关系是:
    //当我需要name类的时候你就给我实例化Name类。
    
    make(name)方法是对name进行生产返回一个实例。

    如果我们要更换键盘怎么办呢?

    $container->bind('Board', function($container){
            return new MechanicalBoard;
        });
    
        $container->bind('Computer',function($container,$module){
            return new Computer($container->make($module));
        });
    
        $computer = $container->make('Computer',['Board']);

    只要对 bind 绑定的 Board 类的实现进行修改,我们就可以很容易替换掉键盘了。这就是一个服务容器。

     参考

  • 相关阅读:
    作业三:代码规范、代码复审、PSP
    第二周学习进度表
    作业二:Github注册过程
    作业二:源程序版本管理软件和项目管理软件的优缺点
    作业二:四则运算
    408笔记--操作系统
    408笔记--计算机网络
    408笔记--计算机组成原理
    408笔记--数据结构
    2020 PAT秋季甲级考试(91分)-记
  • 原文地址:https://www.cnblogs.com/webclz/p/10730944.html
Copyright © 2011-2022 走看看