• 控制反转与依赖注入


    传统的思路是应用程序用到一个Foo类,就会创建Foo类并调用Foo类的方法,假如这个方法内需要一个Bar类,就会创建Bar类并调用Bar类的方法,而这个方法内需要一个Bim类,就会创建Bim类,接着做些其它工作。

        // 代码【1】
     class Bim
        {
            public function doSomething()
            {
                echo __METHOD__, '|';
            }
        }
        
        class Bar
        {
            public function doSomething()
            {
                $bim = new Bim();
                $bim->doSomething();
                echo __METHOD__, '|';
            }
        }
        
        class Foo
        {
            public function doSomething()
            {
                $bar = new Bar();
                $bar->doSomething();
                echo __METHOD__;
            }
        }
        
        $foo = new Foo();
        $foo->doSomething(); //Bim::doSomething|Bar::doSomething|Foo::doSomething
    使用依赖注入的思路是应用程序用到Foo类,Foo类需要Bar类,Bar类需要Bim类,那么先创建Bim类,再创建Bar类并把Bim注入,再创建Foo类,并把Bar类注入,再调用Foo方法,Foo调用Bar方法,接着做些其它工作。
    
        // 代码【2】
        class Bim
        {
            public function doSomething()
            {
                echo __METHOD__, '|';
            }
        }
        
        class Bar
        {
            private $bim;
        
            public function __construct(Bim $bim)
            {
                $this->bim = $bim;
            }
        
            public function doSomething()
            {
                $this->bim->doSomething();
                echo __METHOD__, '|';
            }
        }
        
        class Foo
        {
            private $bar;
        
            public function __construct(Bar $bar)
            {
                $this->bar = $bar;
            }
        
            public function doSomething()
            {
                $this->bar->doSomething();
                echo __METHOD__;
            }
        }
        
        $foo = new Foo(new Bar(new Bim()));
        $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
    

      

    这就是控制反转模式。依赖关系的控制反转到调用链的起点。这样你可以完全控制依赖关系,通过调整不同的注入对象,来控制程序的行为。例如Foo类用到了memcache,可以在不修改Foo类代码的情况下,改用redis。

    使用依赖注入容器后的思路是应用程序需要到Foo类,就从容器内取得Foo类,容器创建Bim类,再创建Bar类并把Bim注入,再创建Foo类,并把Bar注入,应用程序调用Foo方法,Foo调用Bar方法,接着做些其它工作.

    总之容器负责实例化,注入依赖,处理依赖关系等工作。

    代码演示 依赖注入容器 (dependency injection container)

    通过一个最简单的容器类来解释一下,这段代码来自 Twittee

        class Container
        {
            private $s = array();
        
            function __set($k, $c)
            {
                $this->s[$k] = $c;
            }
        
            function __get($k)
            {
                return $this->s[$k]($this);
            }
        }
    

      

    这段代码使用了魔术方法,在给不可访问属性赋值时,__set() 会被调用。读取不可访问属性的值时,__get() 会被调用。

     $c = new Container();
        
        $c->bim = function () {
            return new Bim();
        };
        $c->bar = function ($c) {
            return new Bar($c->bim);
        };
        $c->foo = function ($c) {
            return new Foo($c->bar);
        };
        
        // 从容器中取得Foo
        $foo = $c->foo;
        $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
    

      

    这段代码使用了匿名函数

    再来一段简单的代码演示一下,容器代码来自simple di container

      class IoC
        {
            protected static $registry = [];
        
            public static function bind($name, Callable $resolver)
            {
                static::$registry[$name] = $resolver;
            }
        
            public static function make($name)
            {
                if (isset(static::$registry[$name])) {
                    $resolver = static::$registry[$name];
                    return $resolver();
                }
                throw new Exception('Alias does not exist in the IoC registry.');
            }
        }
        
        IoC::bind('bim', function () {
            return new Bim();
        });
        IoC::bind('bar', function () {
            return new Bar(IoC::make('bim'));
        });
        IoC::bind('foo', function () {
            return new Foo(IoC::make('bar'));
        });
        
        
        // 从容器中取得Foo
        $foo = IoC::make('foo');
        $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
    

      

    这段代码使用了后期静态绑定

    依赖注入容器 (dependency injection container) 高级功能

    真实的dependency injection container会提供更多的特性,如

    • 自动绑定(Autowiring)或 自动解析(Automatic Resolution)

    • 注释解析器(Annotations)

    • 延迟注入(Lazy injection)

    下面的代码在Twittee的基础上,实现了Autowiring。

       class Bim
        {
            public function doSomething()
            {
                echo __METHOD__, '|';
            }
        }
        
        class Bar
        {
            private $bim;
        
            public function __construct(Bim $bim)
            {
                $this->bim = $bim;
            }
        
            public function doSomething()
            {
                $this->bim->doSomething();
                echo __METHOD__, '|';
            }
        }
        
        class Foo
        {
            private $bar;
        
            public function __construct(Bar $bar)
            {
                $this->bar = $bar;
            }
        
            public function doSomething()
            {
                $this->bar->doSomething();
                echo __METHOD__;
            }
        }
        
        class Container
        {
            private $s = array();
        
            public function __set($k, $c)
            {
                $this->s[$k] = $c;
            }
        
            public function __get($k)
            {
                // return $this->s[$k]($this);
                return $this->build($this->s[$k]);
            }
        
            /**
             * 自动绑定(Autowiring)自动解析(Automatic Resolution)
             *
             * @param string $className
             * @return object
             * @throws Exception
             */
            public function build($className)
            {
                // 如果是匿名函数(Anonymous functions),也叫闭包函数(closures)
                if ($className instanceof Closure) {
                    // 执行闭包函数,并将结果
                    return $className($this);
                }
        
                /** @var ReflectionClass $reflector */
                $reflector = new ReflectionClass($className);
        
                // 检查类是否可实例化, 排除抽象类abstract和对象接口interface
                if (!$reflector->isInstantiable()) {
                    throw new Exception("Can't instantiate this.");
                }
        
                /** @var ReflectionMethod $constructor 获取类的构造函数 */
                $constructor = $reflector->getConstructor();
        
                // 若无构造函数,直接实例化并返回
                if (is_null($constructor)) {
                    return new $className;
                }
        
                // 取构造函数参数,通过 ReflectionParameter 数组返回参数列表
                $parameters = $constructor->getParameters();
        
                // 递归解析构造函数的参数
                $dependencies = $this->getDependencies($parameters);
        
                // 创建一个类的新实例,给出的参数将传递到类的构造函数。
                return $reflector->newInstanceArgs($dependencies);
            }
        
            /**
             * @param array $parameters
             * @return array
             * @throws Exception
             */
            public function getDependencies($parameters)
            {
                $dependencies = [];
        
                /** @var ReflectionParameter $parameter */
                foreach ($parameters as $parameter) {
                    /** @var ReflectionClass $dependency */
                    $dependency = $parameter->getClass();
        
                    if (is_null($dependency)) {
                        // 是变量,有默认值则设置默认值
                        $dependencies[] = $this->resolveNonClass($parameter);
                    } else {
                        // 是一个类,递归解析
                        $dependencies[] = $this->build($dependency->name);
                    }
                }
        
                return $dependencies;
            }
        
            /**
             * @param ReflectionParameter $parameter
             * @return mixed
             * @throws Exception
             */
            public function resolveNonClass($parameter)
            {
                // 有默认值则返回默认值
                if ($parameter->isDefaultValueAvailable()) {
                    return $parameter->getDefaultValue();
                }
        
                throw new Exception('I have no idea what to do here.');
            }
        }
        
        // ----
        $c = new Container();
        $c->bar = 'Bar';
        $c->foo = function ($c) {
            return new Foo($c->bar);
        };
        // 从容器中取得Foo
        $foo = $c->foo;
        $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
        
        // ----
        $di = new Container();
        
        $di->foo = 'Foo';
        
        /** @var Foo $foo */
        $foo = $di->foo;
        
        var_dump($foo);
        /*
        Foo#10 (1) {
          private $bar =>
          class Bar#14 (1) {
            private $bim =>
            class Bim#16 (0) {
            }
          }
        }
        */
        
        $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
    

      

    以上代码的原理参考PHP官方文档:反射,PHP 5 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。

    若想进一步提供一个数组访问接口,如$di->foo可以写成$di'foo'],则需用到[ArrayAccess(数组式访问)接口 

    转载于:https://www.cnblogs.com/yaqiangyinsi/p/6178584.html

  • 相关阅读:
    Selenium 疑问之一:WebDriver 获得弹出窗口(转)
    Selenium 疑问之三:Selenium2.0 对于Wrapped元素的click()不起作用怎么办?
    [转]网站性能工具Yslow的使用方法
    Hudson + Ant + SVN + Tomcat配置详解
    Selenium 疑问之二:如何使页面滚动条移动到指定元素element的位置处?
    【转】组合测试法中的全对偶测试法
    Ant 之property 总结
    [转载]使用Selenium2测试含有iframe的Ajax网页
    Nginx反向代理WebSocket链接失败问题
    WebSocket语法糖
  • 原文地址:https://www.cnblogs.com/twodog/p/12141508.html
Copyright © 2020-2023  润新知