zoukankan      html  css  js  c++  java
  • Laravel框架下容器Container 的依赖注入和反射应用

      依赖注入,简单说是把类里头依赖的对象,置于类外头,即客户端调用处。相当于把类与类解耦。

      一个简单的例子:

      

    class A {
    
        public function __construct() {
            // 这种实例化写法, A类的内部,依赖了B类 需要解耦
            $this->b = new B();
        }
    
        public function say(){
            $this->b->talk();
        }
    }
    
    
    class B{
    
        public function __construct() {
    
        }
    
        public function talk(){
            echo __METHOD__;
        }
    }
    
    
    $a = new A;
    $a->say();

      A类依赖B类,耦合较重。

      换一种写法:

      

    class A {
    
        public function __construct(B $b) {// B的实例化,放在客户端,当作构造参数传递过来
            $this->b = $b;
        }
    
        public function say(){
            $this->b->talk();
        }
    }
    
    
    class B{
        public function __construct() {
        }
    
        public function talk(){
            echo __METHOD__;
        }
    }
    
    
    $b = new B;// 在客户端实例化B
    $a = new A($b);// 构造器中传递
    $a->say();

    这个代码,就相当于实例化B的步骤放在了A类的外部,从而实现解耦。这就是依赖注入的一种实现方式。

    Laravel框架中,通过这种依赖注入的方式,再配合反射功能,实现功能。

    比如在Laravel的容器类,Container.php(vendor/laravel/framework/src/illuminate/Container/Container.php)中

    利用反射,实例化注入的类。

    来一段代码:

    /**
     *
     * 依赖注入的思想,把外部依赖的类放在构造函数的参数里
     *
     * Laravel框架根据反射 搞定依赖
     */
    
    class Test{
    
        public function __construct(Para $p1, Parb $p2) {
            echo $p1->getInfo()," ",$p2->getInfo()," ";
        }
    
        function say(){
            echo __CLASS__;
            return;
        }
    }
    
    /**
     * Class Para
     * 参数A
     */
    class Para {
    
        function getInfo() {
            echo __CLASS__;
        }
    }
    
    /**
     * Class Parb
     * 参数B
     */
    class Parb {
    
        function getInfo(){
            echo __CLASS__;
        }
    }
    
    
    $obj = new Test(new Para(), new Parb());
    
    // ===================================================
    
    $reflector = new ReflectionClass($obj);// 对象 反射其类信息
    $constructor = $reflector->getConstructor();
    
    $dependencies = $constructor->getParameters();// 获取构造器下的参数信息
    $parArr = array();
    foreach ($dependencies as $depend){
        // $depend->getClass()->name 获取类名称 构造器参数是类
        $parArr[] = new ($depend->getClass()->name)();
    }
    
    $refNew = $reflector->newInstanceArgs($parArr);// Test 对象
    
    $refNew->say();

    代码中,获取$obj反射后的类信息,再取其构造器中的依赖类,实例化这些依赖类,再传入Test类中。  

    这就是Laravel容器中实现的依赖注入和反射应用。

    下边的代码有助于理解Laravel框架下的容器Container概念。参考自文章:https://www.insp.top/article/learn-laravel-container

    <?php
    
    
    interface SuperModuleInterface{
        public function activate(array $target);
    }
    
    class Superman
    {
        protected $module;
    
        /**
         * Superman constructor.
         * @param SuperModuleInterface $module
         * 通过构造器 注入依赖
         */
        public function __construct(SuperModuleInterface $module)
        {
            $this->module = $module;
        }
    
        public function show(array $target){
    
            $this->module->activate($target);
        }
    }
    
    class PowerA implements SuperModuleInterface
    {
        public function activate(array $target)
        {
            echo '<pre>'. __METHOD__;
            print_r(func_get_args());
        }
    }
    
    class PowerB implements SuperModuleInterface
    {
        public function activate(array $target)
        {
            echo '<pre>'. __METHOD__;
            print_r(func_get_args());
        }
    }
    
    
    
    class Container
    {
        protected $binds;
    
        protected $instances;
    
        /**
         * @param $abstract
         * @param $concrete
         * 把代词 绑定到容器里,为后续make
         */
        public function bind($abstract, $concrete)
        {
            if ($concrete instanceof Closure) {
                $this->binds[$abstract] = $concrete;
            } else {
                $this->instances[$abstract] = $concrete;
            }
        }
    
        /**
         * @param $abstract
         * @param array $parameters
         * @return mixed
         * 创建对象
         */
        public function make($abstract, $parameters = [])
        {
            if (isset($this->instances[$abstract])) {
                return $this->instances[$abstract];
            }
            // 把容器对象$this,放到参数数组第一个元素。为call_user_func_array使用
            array_unshift($parameters, $this);
            
            // 这里$this->binds[$abstract] 绑定的闭包函数, 执行函数参数是$parameters
            return call_user_func_array($this->binds[$abstract], $parameters);
        }
    }
    
    
    // 创建一个容器(后面称作超级工厂)
    $container = new Container;
    
    
    // 向该 超级工厂添加超能力模组的生产脚本
    $container->bind('powerA', function($container) {
        return new PowerA;
    });
    
    // 同上
    $container->bind('powerB', function($container) {
        return new PowerB;
    });
    
    
    // 向该 超级工厂添加超人的生产脚本
    $container->bind('superman', function($container, $moduleName) {
        return new Superman($container->make($moduleName));
    });
    
    
    
    echo "<pre>";
    
    // 开始启动生产
    $superman_1 = $container->make('superman', ['powerA']);
    $superman_1->show(['a' => 1]);
    
    $superman_2 = $container->make('superman', ['powerB']);
    $superman_2->show(['b' => 1]);
  • 相关阅读:
    react-native ios打包和Android打包
    用create-react-app创建的react项目怎么解决打包体积过大
    react native 新建项目运行报错
    微信分享等配置,微信授权失败讲解
    微信公众号的code,微信授权
    linux 递归删除指定文件
    mariaDB安装 无密码就可以登录、远程访问、及新版本初始化密码
    mysql 可重复插入、创建语句
    redis数据库入门
    Extjs 3.4 同值合并
  • 原文地址:https://www.cnblogs.com/firstForEver/p/7976969.html
Copyright © 2011-2022 走看看