zoukankan      html  css  js  c++  java
  • PHP控制反转(IOC)和依赖注入(DI)

    先上一个例子:

    <?php
     
    class A
    {
        public $b;
        public $c;
        public function __construct()
        {
            //代码
        }
        public function Method()
        {
            $this->b=new B();
            $this->c=new C();
             
            $this->b->Method();
            $this->c->Method();
             
            //代码
        }
    }
     
    class B
    {
        public function __construct()
        {
            //代码
        }
        public function Method()
        {
            //代码
            echo 'b';
        }
    }
     
    class C
    {
        public function __construct()
        {
            //代码
        }
        public function Method()
        {
            //代码
            echo 'c';
        }
    }
     
    $a=new A();
    $a->Method();
     
    ?>

    这段代码,我们很容易理解一句话:

    A类依赖B类和C类

    也就是说,如果今后开发过程中,要对B类或者C类修改,一旦涉及函数改名,函数参数数量变动,甚至整个类结构的调整,我们也要对A类做出相应的调整,A类的独立性丧失了,这在开发过程中是很不方便

    如果真要改动B类和C类,有没有办法,可以不去改动或者尽量少改动A类的代码呢?

    答:用控制反转。“高层模块不应该依赖于底层模块,两个都应该依赖抽象。”

    控制反转(IOC)是一种思想,依赖注入(DI)是实施这种思想的方法。

    第一种方法叫做:构造器注入(这种方法也不推荐用,但比不用要好)

    class A
    {
        public $b;
        public $c;
        public function __construct($b,$c)
        {
            $this->b=$b;
            $this->c=$c;
        }
        public function Method()
        {
            $this->b->Method();
            $this->c->Method();
        }
    }
    
    //客户端类
    $a=new A(new B(),new C());
    $a->Method();

    A类的构造器依赖B类和C类,通过构造器的参数传入,至少实现了一点,就是B类对象b和C类对象c的创建都移至了A类外,所以一旦B类和C类发生改动,A类无需做修改,只要在客户端类里改就可以了

    如果我们需要扩充B类,做 一个B类的子类

    class B
    {
        public function __construct()
        {
            //代码
        }
        public function Method()
        {
            //代码
            echo 'b';
        }
    }
    class B1 extends B
    {
        public function __construct()
        {
            //代码
        }
        public function Method()
        {
            echo 'b1';
        }
    }

    客户端类这么写:

    $a=new A(new B1(),new C());
    $a->Method();

    所以A类是不用关心B类到底有哪些个子类的,只要在客户端类关心就可以了。

    第二种方法:工厂模式注入 

    class Factory
    {
        public function __construct()
        {
            //代码
        }
        public function create($s)
        {
            switch($s)
            {
                case 'B':
                {
                    return new B();
                    break;
                }
                case 'C':
                {
                    return new C();
                    break;
                }
                default:
                {
                    return null;
                    break;
                }
            }
        }
    }

    A类代码改为:

    class A
    {
        public $b;
        public $c;
        public function __construct()
        {
            //代码
        }
        public function Method()
        {
            $f=new Factory();
            $this->b=$f->create('B');
            $this->c=$f->create('C');
             
            $this->b->Method();
            $this->c->Method();
             
            //代码
        }
    }

    其实已经解耦了一小部分,至少如果B类和C类的构造函数要是发生变化,比如修改函数参数等,我们只需要改Factory类就可以了。

    把B类和C类中的方法再抽象出来,做一个接口

    interface IMethod
    {
        public function Method();
    }

    这样,A类中的b变量和c变量就不再是一个具体的变量了,而是一个抽象类型的变量,不到运行那一刻,不知道他们的Method方式是怎么实现的。

    class B implements IMethod
    {
        public function __construct()
        {
            //代码
        }
        public function Method()
        {
            //代码
            echo 'b';
        }
    }
     
    class C implements IMethod
    {
        public function __construct()
        {
            //代码
        }
        public function Method()
        {
            //代码
            echo 'c';
        }
    }

    总结两点:

    1.我们把A类中的B类对象和C类对象的创建移至A类外

    2.原本A类依赖B类和C类,现在变成了A依赖Factory,Factory依赖B和C。

    控制反转 是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection, DI), 还有一种叫"依赖查找"(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

    容器:

    是container的一个具体实例,最好还是不要把具体的某个依赖注入写成方法,采用registry注册,get获取比较好

    <?php

    class Ioc {

    /**

    * @var 注册的依赖数组

    */

      

      protected static $registry = array();

      

      /**

      * 添加一个resolve到registry数组中

      * @param string $name 依赖标识

      * @param object $resolve 一个(强类型闭包)匿名函数用来创建实例

      * @return void

      */

      public static function register($name, Closure $resolve)

      {

       static::$registry[$name] = $resolve;

      }

      

      /**

       * 返回一个实例

       * @param string $name 依赖的标识

       * @return mixed

       */

      public static function resolve($name)

      {

        if ( static::registered($name) )

        {

         $name = static::$registry[$name];

         return $name();

        }

        throw new Exception('Nothing registered with that name, fool.');

      }

      /**

      * 查询某个依赖实例是否存在

      * @param string $name id

      * @return bool

      */

      public static function registered($name)

      {

       return array_key_exists($name, static::$registry);

      }

    }

    现在就可以通过如下方式来注册和注入一个

    <?php

    $book = Ioc::registry('book', function(){

    $book = new Book;

    $book->setdb('...');

    $book->setprice('...');

    return $book;

    });

      

    //注入依赖

    $book = Ioc::resolve('book');

    ?>

    laravel中的依赖注入

    服务容器 (Service Container)

    如果我们仔细研究了Service Container我们就会发现laravel的服务容器中只存储了对象的描述,而并不需要知道如何具体的去构造一个对象,因为它会根据php的反射服务去自动解析具体化一个对象。

    反射:

           在计算机科学中,反射是指计算机在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。用来比喻说,那种程序能够“观察”并且修改自己的行为。

    支持反射的语言提供了一些在低级语言中难以实现的运行时特性。这些特性包括:

    1、作为一个第一类对象发现并修改源代码的结构(如代码块、类、方法、协议等)。

    2、将跟class或function匹配的转换成class或function的调用或引用。

    3、在运行时像对待源代码语句一样计算字符串。

    4、创建一个新的语言字节码解释器来给编程结构一个新的意义或用途。

    1、如何实现依赖的自动注入? (控制反转,利用反射)

    2、依赖注入需要哪些东东? (整理依赖关系[ construct | setter ],还要解析依赖传递引用)

    3、怎么解析依赖?

  • 相关阅读:
    20080619 SQL SERVER 输入 NULL 的快捷键
    20090406 Adobe的“此产品的许可已停止工作”错误的解决办法
    20080908 Office Powerpoint 2007 不能输入中文的解决办法
    20080831 ClearGertrude Blog Skin 's cnblogs_code class
    20080603 Facebook 平台正式开放
    20080519 安装 Microsoft SQL Server 2000 时提示 创建挂起的文件操作
    test
    Linux—fork函数学习笔记
    SOA的设计理念
    Why BCP connects to SQL Server instance which start with account of Network Service fail?
  • 原文地址:https://www.cnblogs.com/setevn/p/7794341.html
Copyright © 2011-2022 走看看