• 【设计思想】依赖注入


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

    【代码1】

     1 class Bim
     2     {
     3         public function doSomething()
     4         {
     5             echo __METHOD__, '|';
     6         }
     7     }
     8     
     9     class Bar
    10     {
    11         public function doSomething()
    12         {
    13             $bim = new Bim();
    14             $bim->doSomething();
    15             echo __METHOD__, '|';
    16         }
    17     }
    18     
    19     class Foo
    20     {
    21         public function doSomething()
    22         {
    23             $bar = new Bar();
    24             $bar->doSomething();
    25             echo __METHOD__;
    26         }
    27     }
    28     
    29     $foo = new Foo();
    30     $foo->doSomething(); //Bim::doSomething|Bar::doSomething|Foo::doSomething
    View Code

     使用依赖注入的思路是应用程序用到Foo类,Foo类需要Bar类,Bar类需要Bim类,那么先创建Bim类,再创建Bar类并把Bim注入,再创建Foo类,并把Bar类注入,再调用Foo方法,Foo调用Bar方法,接着做些其它工作。

     1 class Bim
     2     {
     3         public function doSomething()
     4         {
     5             echo __METHOD__, '|';
     6         }
     7     }
     8     
     9     class Bar
    10     {
    11         private $bim;
    12     
    13         public function __construct(Bim $bim)
    14         {
    15             $this->bim = $bim;
    16         }
    17     
    18         public function doSomething()
    19         {
    20             $this->bim->doSomething();
    21             echo __METHOD__, '|';
    22         }
    23     }
    24     
    25     class Foo
    26     {
    27         private $bar;
    28     
    29         public function __construct(Bar $bar)
    30         {
    31             $this->bar = $bar;
    32         }
    33     
    34         public function doSomething()
    35         {
    36             $this->bar->doSomething();
    37             echo __METHOD__;
    38         }
    39     }
    40     
    41     $foo = new Foo(new Bar(new Bim()));
    42     $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
    View Code

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

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

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

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

    通过一个最简单的容器类来解释一下

     1 class Container
     2     {
     3         private $s = array();
     4     
     5         function __set($k, $c)
     6         {
     7             $this->s[$k] = $c;
     8         }
     9     
    10         function __get($k)
    11         {
    12             return $this->s[$k]($this);
    13         }
    14     }
    View Code

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

     1 $c = new Container();
     2     
     3     $c->bim = function () {
     4         return new Bim();
     5     };
     6     $c->bar = function ($c) {
     7         return new Bar($c->bim);
     8     };
     9     $c->foo = function ($c) {
    10         return new Foo($c->bar);
    11     };
    12     
    13     // 从容器中取得Foo
    14     $foo = $c->foo;
    15     $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
    View Code

    这段代码使用了匿名函数

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

     1 class IoC
     2     {
     3         protected static $registry = [];
     4     
     5         public static function bind($name, Callable $resolver)
     6         {
     7             static::$registry[$name] = $resolver;
     8         }
     9     
    10         public static function make($name)
    11         {
    12             if (isset(static::$registry[$name])) {
    13                 $resolver = static::$registry[$name];
    14                 return $resolver();
    15             }
    16             throw new Exception('Alias does not exist in the IoC registry.');
    17         }
    18     }
    19     
    20     IoC::bind('bim', function () {
    21         return new Bim();
    22     });
    23     IoC::bind('bar', function () {
    24         return new Bar(IoC::make('bim'));
    25     });
    26     IoC::bind('foo', function () {
    27         return new Foo(IoC::make('bar'));
    28     });
    29     
    30     
    31     // 从容器中取得Foo
    32     $foo = IoC::make('foo');
    33     $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
    View Code

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

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

      1 class Bim
      2     {
      3         public function doSomething()
      4         {
      5             echo __METHOD__, '|';
      6         }
      7     }
      8     
      9     class Bar
     10     {
     11         private $bim;
     12     
     13         public function __construct(Bim $bim)
     14         {
     15             $this->bim = $bim;
     16         }
     17     
     18         public function doSomething()
     19         {
     20             $this->bim->doSomething();
     21             echo __METHOD__, '|';
     22         }
     23     }
     24     
     25     class Foo
     26     {
     27         private $bar;
     28     
     29         public function __construct(Bar $bar)
     30         {
     31             $this->bar = $bar;
     32         }
     33     
     34         public function doSomething()
     35         {
     36             $this->bar->doSomething();
     37             echo __METHOD__;
     38         }
     39     }
     40     
     41     class Container
     42     {
     43         private $s = array();
     44     
     45         public function __set($k, $c)
     46         {
     47             $this->s[$k] = $c;
     48         }
     49     
     50         public function __get($k)
     51         {
     52             // return $this->s[$k]($this);
     53             return $this->build($this->s[$k]);
     54         }
     55     
     56         /**
     57          * 自动绑定(Autowiring)自动解析(Automatic Resolution)
     58          *
     59          * @param string $className
     60          * @return object
     61          * @throws Exception
     62          */
     63         public function build($className)
     64         {
     65             // 如果是匿名函数(Anonymous functions),也叫闭包函数(closures)
     66             if ($className instanceof Closure) {
     67                 // 执行闭包函数,并将结果
     68                 return $className($this);
     69             }
     70     
     71             /** @var ReflectionClass $reflector */
     72             $reflector = new ReflectionClass($className);
     73     
     74             // 检查类是否可实例化, 排除抽象类abstract和对象接口interface
     75             if (!$reflector->isInstantiable()) {
     76                 throw new Exception("Can't instantiate this.");
     77             }
     78     
     79             /** @var ReflectionMethod $constructor 获取类的构造函数 */
     80             $constructor = $reflector->getConstructor();
     81     
     82             // 若无构造函数,直接实例化并返回
     83             if (is_null($constructor)) {
     84                 return new $className;
     85             }
     86     
     87             // 取构造函数参数,通过 ReflectionParameter 数组返回参数列表
     88             $parameters = $constructor->getParameters();
     89     
     90             // 递归解析构造函数的参数
     91             $dependencies = $this->getDependencies($parameters);
     92     
     93             // 创建一个类的新实例,给出的参数将传递到类的构造函数。
     94             return $reflector->newInstanceArgs($dependencies);
     95         }
     96     
     97         /**
     98          * @param array $parameters
     99          * @return array
    100          * @throws Exception
    101          */
    102         public function getDependencies($parameters)
    103         {
    104             $dependencies = [];
    105     
    106             /** @var ReflectionParameter $parameter */
    107             foreach ($parameters as $parameter) {
    108                 /** @var ReflectionClass $dependency */
    109                 $dependency = $parameter->getClass();
    110     
    111                 if (is_null($dependency)) {
    112                     // 是变量,有默认值则设置默认值
    113                     $dependencies[] = $this->resolveNonClass($parameter);
    114                 } else {
    115                     // 是一个类,递归解析
    116                     $dependencies[] = $this->build($dependency->name);
    117                 }
    118             }
    119     
    120             return $dependencies;
    121         }
    122     
    123         /**
    124          * @param ReflectionParameter $parameter
    125          * @return mixed
    126          * @throws Exception
    127          */
    128         public function resolveNonClass($parameter)
    129         {
    130             // 有默认值则返回默认值
    131             if ($parameter->isDefaultValueAvailable()) {
    132                 return $parameter->getDefaultValue();
    133             }
    134     
    135             throw new Exception('I have no idea what to do here.');
    136         }
    137     }
    138     
    139     // ----
    140     $c = new Container();
    141     $c->bar = 'Bar';
    142     $c->foo = function ($c) {
    143         return new Foo($c->bar);
    144     };
    145     // 从容器中取得Foo
    146     $foo = $c->foo;
    147     $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
    148     
    149     // ----
    150     $di = new Container();
    151     
    152     $di->foo = 'Foo';
    153     
    154     /** @var Foo $foo */
    155     $foo = $di->foo;
    156     
    157     var_dump($foo);
    158     /*
    159     Foo#10 (1) {
    160       private $bar =>
    161       class Bar#14 (1) {
    162         private $bim =>
    163         class Bim#16 (0) {
    164         }
    165       }
    166     }
    167     */
    168     
    169     $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
    View Code

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

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

    一些复杂的容器会有许多特性

    工欲善其事,必先利其器!
  • 相关阅读:
    一个漂亮的lazarus做的pagecontrol
    预测,阿里盒子必将失败!
    sex在软件开发中的运用--SIX技术
    糟糕的@@identity,SCOPE_IDENTITY ,IDENT_CURRENT
    Delphi、Lazarus保留字、关键字详解
    糟糕的界面设计
    Firebird存储过程--更加人性化的设计
    lazarus的动态方法和虚拟方法
    用户行为导向的交互设计
    Javascript的一个怪现象
  • 原文地址:https://www.cnblogs.com/caiyunyun/p/7506610.html
Copyright © 2020-2023  润新知