精华内容
下载资源
问答
  • 本文主要介绍了PHP控制反转(IOC)和依赖注入(DI)的相关知识。具有很好的参考价值。下面跟着小编一起来看下吧
  • IOC(inversion of control)控制反转模式;控制反转是将组件间的依赖关系从程序内部提到外部来管理; DI(dependency injection)依赖注入模式;依赖注入是指将组件的依赖通过外部以参数或其他形式注入; 两个说法...

    IOC(inversion of control)控制反转模式;控制反转是将组件间的依赖关系从程序内部提到外部来管理;
    DI(dependency injection)依赖注入模式;依赖注入是指将组件的依赖通过外部以参数或其他形式注入;
    两个说法本质上是一个意思。
    例如:

    class DbMysql
    {
        public function query(){}
    }
    class Controller
    {
        public $db;
        public function __construct()
        {
            $this->db = new DbMysql();
        }
        public function action()
        {
            $this->db->query();
        }
    }
    $c = new Controller();
    $c->action();
    

    Controller类中的action方法需要用到DbMysql类中的query方法,所以Controller类就对DbMysql类产生了依赖,Controller类和DbMysql类之间的耦合度就比较高,因为当DbMysql类的构造函数发生改变的时候,比如由现在的没有参数变成有参数了,参数数量改变了,那么Controller类中的代码都要做出相应改变。

    或者说我们现在需要将DbMysql类换成另一个DbOracle类,Controller类中要做出的改变甚至更大

    看下面的另一种写法:

    class DbMysql
    {
        public function query(){}
    }
    class Controller
    {
        public $db;
        public function __construct($dbMysql)
        {
            $this->db = $dbMysql;
        }
        public function action()
        {
            $this->db->query();
        }
    }
    $db = new DbMysql();
    $c = new Controller($db);
    $c->action();
    

    Controller类中不需要实例化DbMysql,而是将DbMysql类的实例作为参数传递(或者单独写一个接收实例的方法处理),这样Controller类就完全不用管DbMysql是怎么样实例的,而是仅仅调用DbMysql中的query方法就行了。这种模式就是依赖注入。

    第一个例子中Controller类负责实例DbMysql,也就是说Controller类控制着实例DbMysql类的主动权,而第二个例子中将这个主动权提出到Controller类的外面,所以也叫做控制反转。

    这样看起来还不错,但是如果我们需要很多类,而且需要自己写的时候弄清楚,每一个类依赖什么类,这样太烦,如果有一个类能够帮我们搞定这个动作那就太爽了。而事实上有这个类,这个类就叫做IOC容器。

    下面通过实例与大家分析分析

    示例一

    class DbMysql
    {
        public function __construct($host, $name, $pwd)
        {
            // do something
        }
    
        public function query()
        {
            echo __METHOD__ . PHP_EOL;
        }
    }
    
    class DbRedis
    {
        public function __construct($host, $name, $pwd)
        {
            // do something
        }
    
        public function set()
        {
            echo __METHOD__ . PHP_EOL;
        }
    }
    
    class controller
    {
        public $mysql;
        public $redis;
    
        public function __construct()
        {
            $this->mysql = new DbMysql('host', 'name', 'pwd');
            $this->redis = new DbRedis('host', 'name', 'pwd');
        }
    
        public function action()
        {
            $this->mysql->query();
            $this->redis->set();
        }
    }
    
    $c = new Controller();
    $c->action();
    /**
     * 输出:
     * DbMysql::query
     * DbRedis::set
     */
    

    普通的实现方式,耦合度高。

    示例二

    class DbMysql
    {
        public function __construct($host, $name, $pwd)
        {
            // do something
        }
    
        public function query()
        {
            echo __METHOD__ . PHP_EOL;
        }
    }
    
    class DbRedis
    {
        public function __construct($host, $name, $pwd)
        {
            // do something
        }
    
        public function set()
        {
            echo __METHOD__ . PHP_EOL;
        }
    }
    
    class controller
    {
        public $mysql;
        public $redis;
    
        public function __construct($mysql, $redis)
        {
            $this->mysql = $mysql;
            $this->redis = $redis;
        }
    
        public function action()
        {
            $this->mysql->query();
            $this->redis->set();
        }
    }
    
    $mysql = new DbMysql('host', 'name', 'pwd');
    $redis = new DbRedis('host', 'name', 'pwd');
    $c = new Controller($mysql, $redis);
    $c->action();
    /**
     * 输出:
     * DbMysql::query
     * DbRedis::set
     */
    

    实现了依赖注入和控制反转,但是没有使用容器类。

    示例三

    class DbMysql
    {
        public function __construct($host, $name, $pwd)
        {
            // do something
        }
    
        public function query()
        {
            echo __METHOD__ . PHP_EOL;
        }
    }
    
    class DbRedis
    {
        public function __construct($host, $name, $pwd)
        {
            // do something
        }
    
        public function set()
        {
            echo __METHOD__ . PHP_EOL;
        }
    }
    
    class controller
    {
        public $mysql;
        public $redis;
    
        public function __construct($mysql, $redis)
        {
            $this->mysql = $mysql;
            $this->redis = $redis;
        }
    
        public function action()
        {
            $this->mysql->query();
            $this->redis->set();
        }
    }
    
    class Container
    {
    
        public $bindings = [];
    
        public function bind($key, Closure $value)
        {
            $this->bindings[$key] = $value;
        }
    
        public function make($key)
        {
            $new = $this->bindings[$key];
            return $new();
        }
    
    }
    
    $app = new Container();
    $app->bind('mysql', function () {
        return new DbMysql('host', 'name', 'pwd');
    });
    $app->bind('redis', function () {
        return new DbRedis('host', 'name', 'pwd');
    });
    $app->bind('controller', function () use ($app) {
        return new Controller($app->make('mysql'), $app->make('redis'));
    });
    $controller = $app->make('controller');
    $controller->action();
    /**
     * 输出:
     * DbMysql::query
     * DbRedis::set
     */
    

    实现了基本的容器类,容器类中有两个方法,bind和make,一个是绑定操作,一个是实例化操作。将每一个需要使用到的类使用关键字绑定到容器类中去,但是每一个类仍然需要手动去实例化,这里引入了闭包函数,主要作用是在调用的时候才真正去实例化,而如果仅仅是绑定了一个类,是不会实例化这个类的。

    示例四

    class T
    {
        public $t;
    }
    
    class X
    {
        public $x;
    
        private function __construct()
        {
        }
    }
    
    class Y
    {
        public $x;
    
        public function __construct()
        {
        }
    }
    
    interface Is
    {
    }
    
    class Sis implements Is
    {
    
    }
    
    class S
    {
        public $s;
    
        public function __construct(string $s, int $i, array $a, Is $object)
        {
            $this->s = $s;
        }
    }
    
    function reflectionClass($className, array $inParams = [])
    {
        $reflection = new ReflectionClass($className);
        // isInstantiable() 方法判断类是否可以实例化
        $isInstantiable = $reflection->isInstantiable();
        if ($isInstantiable) {
            // getConstructor() 方法获取类的构造函数,为NULL没有构造函数
            $constructor = $reflection->getConstructor();
            if (is_null($constructor)) {
                // 没有构造函数直接实例化对象返回
                return new $className;
            } else {
                // 有构造函数
                $params = $constructor->getParameters();
                if (empty($params)) {
                    // 构造函数没有参数,直接实例化对象返回
                    return new $className;
                } else {
                    // 构造函数有参数,将$inParams传入实例化对象返回
                    return $reflection->newInstanceArgs($inParams);
                }
            }
        }
        return null;
    }
    
    $t = reflectionClass('T');
    var_dump($t instanceof T);
    $x = reflectionClass('X');
    var_dump($x instanceof X);
    $x = reflectionClass('Y');
    var_dump($x instanceof Y);
    $s = reflectionClass('S', ['asdf', 123, [1, 2], (new Sis)]);
    var_dump($s instanceof S);
    /**
     * 输出:
     * bool(true)
     * bool(false)
     * bool(true)
     * bool(true)
     */
    

    引入反射类,他的作用是可以实例化一个类,和new操作一样。但是实例化一个类所需要的参数,他都能自动检测出来。并且能够检测出来这个参数是不是一个继承了接口的类。上面说一个类依赖另一个类,然后将另一个类作为参数注入,这个反射能够检测出一个类实例化的时候需要什么样的类,好像有点眉目了是吧。

    示例五

    class DbMysql
    {
        public function __construct($host, $name, $pwd)
        {
            // do something
        }
    
        public function query()
        {
            echo __METHOD__ . PHP_EOL;
        }
    }
    
    class DbRedis
    {
        public function __construct($host, $name, $pwd)
        {
            // do something
        }
    
        public function set()
        {
            echo __METHOD__ . PHP_EOL;
        }
    }
    
    class controller
    {
        public $mysql;
        public $redis;
    
        public function __construct($mysql, $redis)
        {
            var_dump($mysql);var_dump($redis);
            $this->mysql = $mysql;
            $this->redis = $redis;
        }
    
        public function action()
        {
            is_object($this->mysql) && $this->mysql->query();
            is_object($this->redis) && $this->redis->set();
        }
    }
    
    class Container
    {
    
        public $bindings = [];
    
        public function bind($key, $value)
        {
            if (!$value instanceof Closure) {
                $this->bindings[$key] = $this->getClosure($value);
            }
            else{
                $this->bindings[$key] = $value;
            }
        }
    
        public function getClosure($value)
        {
            return function () use ($value) {
                return $this->build($value);
            };
        }
    
        public function make($key)
        {
            if (isset($this->bindings[$key])) {
                return $this->build($this->bindings[$key]);
            }
            return $this->build($key);
        }
    
        public function build($value)
        {
            if ($value instanceof Closure) {
                return $value();
            }
            // 实例化反射类
            $reflection = new ReflectionClass($value);
            // isInstantiable() 方法判断类是否可以实例化
            $isInstantiable = $reflection->isInstantiable();
            if ($isInstantiable) {
                // getConstructor() 方法获取类的构造函数,为NULL没有构造函数
                $constructor = $reflection->getConstructor();
                if (is_null($constructor)) {
                    // 没有构造函数直接实例化对象返回
                    return new $value;
                } else {
                    // 有构造函数
                    $params = $constructor->getParameters();
                    if (empty($params)) {
                        // 构造函数没有参数,直接实例化对象返回
                        return new $value;
                    } else {
                        $dependencies = [];
                        // 构造函数有参数
                        foreach ($params as $param) {
                            $dependency = $param->getClass();
                            if (is_null($dependency)) {
                                // 构造函数参数不为class,返回NULL
                                $dependencies[] = NULL;
                            } else {
                                // 类存在创建类实例
                                $dependencies[] = $this->make($param->getClass()->name);
                            }
                        }
                        return $reflection->newInstanceArgs($dependencies);
                    }
                }
            }
            return null;
        }
    
    }
    
    $app = new Container();
    $app->bind('mysql', function () {
        return new DbMysql('host', 'name', 'pwd');
    });
    $app->bind('redis', function () {
        return new DbRedis('host', 'name', 'pwd');
    });
    $app->bind('controller', 'controller');
    $controller = $app->make('controller');
    $controller->action();
    /**
     * 输出:
     * NULL
     * NULL
     */
    

    容器类中引入反射,容器类bind方法升级,不仅仅支持闭包绑定,而且支持类名绑定。示例三中的bind方法仅仅支持绑定一个关键字为闭包,而类的实例操作,需要写到闭包函数中去。现在有了反射,可以直接使用类名,反射会根据类名自动去实例化这个类。

    但是这个例子中输出两个NULL,Controller类的两个参数均为NULL,反射类并没有自动去找到Controller依赖的DbMysql和DbRedis去实例化。这是为什么呢?现在就需要引入另一个东西,针对接口编程。

    这个例子中我们知道Controller类的两个参数是类,但是我们定义的构造函数中并没有声明,现在这种定义方式的两个参数,是类,是字符串,是整型,完全没区别的,反射类无法检测出这两个参数是类,我们的反射方法里面如果检测到构造函数的参数不是类直接返回NULL,所以这里输出了两个NULL。

    示例六

    interface SMysql
    {
        public function query();
    }
    
    class DbMysql implements SMysql
    {
        public function __construct($host, $name, $pwd)
        {
            // do something
        }
    
        public function query()
        {
            echo __METHOD__ . PHP_EOL;
        }
    }
    
    interface SRedis
    {
        public function set();
    }
    
    class DbRedis implements SRedis
    {
        public function __construct($host, $name, $pwd)
        {
            // do something
        }
    
        public function set()
        {
            echo __METHOD__ . PHP_EOL;
        }
    }
    
    class controller
    {
        public $mysql;
        public $redis;
    
        public function __construct(SMysql $mysql, SRedis $redis)
        {
            $this->mysql = $mysql;
            $this->redis = $redis;
        }
    
        public function action()
        {
            is_object($this->mysql) && $this->mysql->query();
            is_object($this->redis) && $this->redis->set();
        }
    }
    
    class Container
    {
    
        public $bindings = [];
    
        public function bind($key, $value)
        {
            if (!$value instanceof Closure) {
                $this->bindings[$key] = $this->getClosure($value);
            } else {
                $this->bindings[$key] = $value;
            }
        }
    
        public function getClosure($value)
        {
            return function () use ($value) {
                return $this->build($value);
            };
        }
    
        public function make($key)
        {
            if (isset($this->bindings[$key])) {
                return $this->build($this->bindings[$key]);
            }
            return $this->build($key);
        }
    
        public function build($value)
        {
            if ($value instanceof Closure) {
                return $value();
            }
            // 实例化反射类
            $reflection = new ReflectionClass($value);
            // isInstantiable() 方法判断类是否可以实例化
            $isInstantiable = $reflection->isInstantiable();
            if ($isInstantiable) {
                // getConstructor() 方法获取类的构造函数,为NULL没有构造函数
                $constructor = $reflection->getConstructor();
                if (is_null($constructor)) {
                    // 没有构造函数直接实例化对象返回
                    return new $value;
                } else {
                    // 有构造函数
                    $params = $constructor->getParameters();
                    if (empty($params)) {
                        // 构造函数没有参数,直接实例化对象返回
                        return new $value;
                    } else {
                        $dependencies = [];
                        // 构造函数有参数
                        foreach ($params as $param) {
                            $dependency = $param->getClass();
                            if (is_null($dependency)) {
                                // 构造函数参数不为class,返回NULL
                                $dependencies[] = NULL;
                            } else {
                                // 类存在创建类实例
                                $dependencies[] = $this->make($param->getClass()->name);
                            }
                        }
                        return $reflection->newInstanceArgs($dependencies);
                    }
                }
            }
            return null;
        }
    
    }
    
    $app = new Container();
    $app->bind('SMysql', function () {
        return new DbMysql('host', 'name', 'pwd');
    });
    $app->bind('SRedis', function () {
        return new DbRedis('host', 'name', 'pwd');
    });
    $app->bind('controller', 'controller');
    $controller = $app->make('controller');
    $controller->action();
    /**
     * 输出:
     * DbMysql::query
     * DbRedis::set
     */
    

    使用接口了以后脚本执行正常。

    首先容器绑定了接口SMysql和SRedis分别对应的闭包,绑定的关键字为接口名称,也就是说在容器中多次绑定一个接口只会绑定一个闭包一个类的实现,controller类的构造函数中声明了需要两个分别继承自SMysql接口和SRedis接口的类。容器中已经有了这两个接口的实现方式,所以直接调用闭包函数实例化类,然后将结果提供给controller类进行实例化。

    本例中,我们仅仅make了controller类,也就是说我们只需要实例化controller类,而controller类依赖的DbMysql类和DbRedis类,IOC容器会自动帮我们实例化并注入。

    本例中controller类的实例化很简单:

    $app->bind('controller', 'controller');
    $controller = $app->make('controller');
    

    但是DbMysql和DbRedis就比较丑了:

    $app->bind('SMysql', function () {
        return new DbMysql('host', 'name', 'pwd');
    });
    $app->bind('SRedis', function () {
        return new DbRedis('host', 'name', 'pwd');
    });
    

    其实是这两个类写的有问题,因为这两个类不是面对接口编程。但是这种自定义闭包函数的绑定非常方便,完全满足任何类的自由实例化。我们要做是依赖注入,那就全部使用接口来实现,看下一个例子。

    示例七

    interface MConfig
    {
        public function getConfig();
    }
    
    class MysqlConfig implements MConfig
    {
        public function getConfig()
        {
            // 获取配置
            return ['host', 'name', 'pwd'];
        }
    }
    
    interface RConfig
    {
        public function getConfig();
    }
    
    class RedisConfig implements RConfig
    {
        public function getConfig()
        {
            // 获取配置
            return ['host', 'name', 'pwd'];
        }
    }
    
    interface SMysql
    {
        public function query();
    }
    
    class DbMysql implements SMysql
    {
        public $config;
    
        public function __construct(MConfig $config)
        {
            $this->config = $config->getConfig();
            // do something
        }
    
        public function query()
        {
            echo __METHOD__ . PHP_EOL;
        }
    }
    
    interface SRedis
    {
        public function Set();
    }
    
    class DbRedis implements SRedis
    {
        public function __construct(RConfig $config)
        {
            $this->config = $config->getConfig();
            // do something
        }
    
        public function set()
        {
            echo __METHOD__ . PHP_EOL;
        }
    }
    
    class Controller
    {
        public $mysql;
        public $redis;
    
        public function __construct(SMysql $mysql, SRedis $redis)
        {
            $this->mysql = $mysql;
            $this->redis = $redis;
        }
    
        public function action()
        {
            is_object($this->mysql) && $this->mysql->query();
            is_object($this->redis) && $this->redis->set();
        }
    }
    
    class Container
    {
    
        public $bindings = [];
    
        public function bind($key, $value)
        {
            if (!$value instanceof Closure) {
                $this->bindings[$key] = $this->getClosure($value);
            } else {
                $this->bindings[$key] = $value;
            }
        }
    
        public function getClosure($value)
        {
            return function () use ($value) {
                return $this->build($value);
            };
        }
    
        public function make($key)
        {
            if (isset($this->bindings[$key])) {
                return $this->build($this->bindings[$key]);
            }
            return $this->build($key);
        }
    
        public function build($value)
        {
            if ($value instanceof Closure) {
                return $value();
            }
            // 实例化反射类
            $reflection = new ReflectionClass($value);
            // isInstantiable() 方法判断类是否可以实例化
            $isInstantiable = $reflection->isInstantiable();
            if ($isInstantiable) {
                // getConstructor() 方法获取类的构造函数,为NULL没有构造函数
                $constructor = $reflection->getConstructor();
                if (is_null($constructor)) {
                    // 没有构造函数直接实例化对象返回
                    return new $value;
                } else {
                    // 有构造函数
                    $params = $constructor->getParameters();
                    if (empty($params)) {
                        // 构造函数没有参数,直接实例化对象返回
                        return new $value;
                    } else {
                        $dependencies = [];
                        // 构造函数有参数
                        foreach ($params as $param) {
                            $dependency = $param->getClass();
                            if (is_null($dependency)) {
                                // 构造函数参数不为class,返回NULL
                                $dependencies[] = NULL;
                            } else {
                                // 类存在创建类实例
                                $dependencies[] = $this->make($param->getClass()->name);
                            }
                        }
                        return $reflection->newInstanceArgs($dependencies);
                    }
                }
            }
            return null;
        }
    
    }
    
    $app = new Container();
    $app->bind('MConfig', 'MysqlConfig');
    $app->bind('RConfig', 'RedisConfig');
    $app->bind('SMysql', 'DbMysql');
    $app->bind('SRedis', 'DbRedis');
    $app->bind('controller', 'Controller');
    $controller = $app->make('controller');
    $controller->action();
    /**
     * 输出:
     * DbMysql::query
     * DbRedis::set
     */
    

    我们只需要实例化一个IOC容器,而后我们要使用到的所有类,它都能帮我们自动依赖注入。当然,这种实现是建立在组件针对接口编程上面的,否则就是示例六中的那种。

    以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要请戳这里

    34ded55777cee0fade00e54b7d03fcc9.png

    b4004fce710feae360af770578474dae.png

    767752369af5540d9a839790bb555a56.png

    最后,祝所有大家在面试中过关斩将,拿到心仪offer。如果想与一群3-8年资深开发者一起交流学习的话,需要

    请戳这里shimo.im
    c6a7f305537074c3503642e27ef4fcc9.png
    展开全文
  • 控制反转(Inversion of Control):当调用者需要被调用者的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例,但在这里,创建被调用者的工作不再由调用者来完成,而是将被调用者的创建移到调用者...

    依赖注入的目的是实现松耦合的软件架构,以便更好的测试,管理和扩展的代码。

    控制反转(Inversion of Control):当调用者需要被调用者的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例,但在这里,创建被调用者的工作不再由调用者来完成,而是将被调用者的创建移到调用者的外部,从而反转被调用者的创建,消除了调用者对被调用者创建的控制,因此称为控制反转。

    依赖注入(Dependency Injection):要实现控制反转,通常的解决方案是将创建被调用者实例的工作交由IoC容器来完成,然后在调用者中注入被调用者(通过构造器/方法注入实现),这样我们就实现了调用者与被调用者的解耦,该过程被称为依赖注入。依赖注入是控制反转的一种实现方式。常见注入方式有三种:setter、constructor injection、property injection。

    容器(Container):管理对象的生成、资源取得、销毁等生命周期,建立对象与对象之间的依赖关系,可以延时加载对象。比较著名有PHP-DI、Pimple。

    代码演示IoC:

    假设应用程序有储存需求,若直接在高层的应用程序中调用低层模块API,导致应用程序对低层模块产生依赖。

    <?php
    /**
     * 高层
     */
    class App
    {
        private $writer;
    
        public function __construct()
        {
            $this->writer = new FloppyWriter();
        }
    
        public function save()
        {
            $this->writer->saveToFloppy();
        }
    }
    
    /**
     * 低层,软盘存储
     */
    class FloppyWriter
    {
        public function saveToFloppy()
        {
            echo __METHOD__;
        }
    }
    
    $app = new App();
    $app->save(); // FloppyWriter::saveToFloppy
    假设程序要移植到另一个平台,而该平台使用USB磁盘作为存储介质,则这个程序无法直接重用,必须加以修改才行。本例由于低层变化导致高层也跟着变化,不好的设计。程序不应该依赖于具体的实现,而是要依赖抽像的接口。请看代码演示:

    <?php
    /**
     * 接口
     */
    interface IDeviceWriter
    {
        public function saveToDevice();
    }
    
    /**
     * 高层
     */
    class App
    {
        /**
         * @var IDeviceWriter
         */
        private $writer;
    
        /**
         * @param IDeviceWriter $writer
         */
        public function setWriter($writer)
        {
            $this->writer = $writer;
        }
    
        public function save()
        {
            $this->writer->saveToDevice();
        }
    }
    
    /**
     * 低层,软盘存储
     */
    class FloppyWriter implements IDeviceWriter
    {
    
        public function saveToDevice()
        {
            echo __METHOD__;
        }
    }
    
    /**
     * 低层,USB盘存储
     */
    class UsbDiskWriter implements IDeviceWriter
    {
    
        public function saveToDevice()
        {
            echo __METHOD__;
        }
    }
    
    $app = new App();
    $app->setWriter(new UsbDiskWriter());
    $app->save(); // UsbDiskWriter::saveToDevice
    
    $app->setWriter(new FloppyWriter());
    $app->save(); // FloppyWriter::saveToDevice
    控制权从实际的FloppyWriter转移到了抽象的IDeviceWriter接口上,让App依赖于IDeviceWriter接口,且FloppyWriter、UsbDiskWriter也依赖于IDeviceWriter接口。
    这就是IoC,面对变化,高层不用修改一行代码,不再依赖低层,而是依赖注入,这就引出了DI。

    如果这个组件有很多依赖,我们需要创建多个参数的setter方法​​来传递依赖关系,这让我们的代码不易维护。

    <?php
    //创建依赖实例
    $request = new Request();
    $filter = new Filter();
    
    //把实例作为参数传递给构造函数
    $some = new SomeComponent($request, $filter);
    
    $some->setRequest($request);
    $some->setFilter($filter);

    解决的方法是为依赖实例提供一个容器。这个容器担任全局的注册表,注入容器而不是具体实例。

    <?php
    class SomeComponent
    {
    
        protected $_di;
    
        public function __construct($di)
        {
            $this->_di = $di;
        }
    
        public function someRequest()
        {
    
            // 请求实例
            $connection = $this->_di->get('request');
    
        }
    
        public function someOtherRequest()
        {
    
            // 请求实例
            $connection = $this->_di->get('request');
    
            // 过滤器实例
            $filter = $this->_di->get('filter');
    
        }
    
    }
    
    $di = new DI();
    
    //在容器中注册一个request服务
    $di->set('request', function() {
        return new Request(array(
            "test" => "test"
        ));
    });
    
    //在容器中注册一个filter服务
    $di->set('filter', function() {
        return new Filter();
    });
    
    //把传递服务的容器作为唯一参数传递给组件
    $some = new SomeComponent($di);
    $some->someRequest();
    这个组件现在可以很简单的获取到它所需要的服务,服务采用延迟加载的方式,只有在需要使用的时候才初始化,这也节省了服务器资源。这个组件现在是高度解耦。



    展开全文
  • 控制反转(IOC)是一种思想,依赖注入(DI)是实施这种思想的方法。 简单实现一个代码依赖: <?php class iocA { public $b; public $c; public function A() { //TODO } public function Method() ...

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

    简单实现一个代码依赖:

    <?php
    
    class iocA
    {
        public $b;
        public $c;
    
        public function A()
        {
            //TODO
        }
    
        public function Method()
        {
            $this->b = new iocB();
            $this->c = new iocC();
    
            $this->b->Method();
            $this->c->Method();
        }
    }
    
    class iocB
    {
        public function C()
        {
            //TODO
        }
    
        public function Method()
        {
            echo 'b';
        }
    }
    
    class iocC
    {
        public function C()
        {
            //TODO
        }
    
        public function Method()
        {
            //TODO
            echo 'c';
        }
    }
    
    $a = new iocA();
    $a->Method();
    

    上边的代码实现了 A 类依赖 B 类和 C 类。

    如果在开发过程中,需要对 B 类或者 C 类进行修改,,一旦涉及到函数名称的改变,或者函数参数数量的变动,或者整个类结构的变化,我们也需要对 A 类进行调整,A 类的独立性就丧失了,这在开发中和不方便,代码复杂,耦合度不尽人意,这样会造成“牵一发动全身”,解决这个问题就需要用到 控制翻转(IOC)。

    依赖注入的实现

    第一种:构造器注入

    <?php
    
    class iocA
    {
        public $b;
        public $c;
    
        public function __construct($iocB, $iocC)
        {
            $this->b = $iocB;
            $this->c = $iocC;
        }
    
        public function Method()
        {
            $this->b->Method();
            $this->c->Method();
        }
    }
    
    class iocB
    {
        public function C()
        {
            //TODO
        }
    
        public function Method()
        {
            echo 'b';
        }
    }
    
    class iocC
    {
        public function C()
        {
            //TODO
        }
    
        public function Method()
        {
            //TODO
            echo 'c';
        }
    }
    
    $a = new iocA(new iocB(), new iocC());
    $a->Method();
    

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

    第二种:工厂模式注入

    <?php
    
    class Factory
    {
        public function Factory()
        {
            //TODO
        }
    
        public function create($s)
        {
            switch ($s) {
                case 'B':
                {
                    return new iocB();
                    break;
                }
                case 'C':
                {
                    return new iocC();
                    break;
                }
                default:
                {
                    return null;
                    break;
                }
            }
        }
    }
    
    class iocA
    {
        public $b;
        public $c;
    
        public function __construct()
        {
            //TODO
        }
    
        public function Method()
        {
            $factory = new Factory();
            $this->b = $factory->create('B');
            $this->c = $factory->create('C');
    
            $this->b->Method();
            $this->c->Method();
        }
    }
    
    class iocB
    {
        public function C()
        {
            //TODO
        }
    
        public function Method()
        {
            echo 'b';
        }
    }
    
    class iocC
    {
        public function C()
        {
            //TODO
        }
    
        public function Method()
        {
            //TODO
            echo 'c';
        }
    }
    
    $a = new iocA();
    $a->Method();
    

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

    抽象不应该依赖于细节,细节应该依赖于抽象。

    展开全文
  • 现在我们来讲讲PHP的另外一种设计模式,控制反转/依赖注入,这两者其实是同一个概念,只是凶不同的角度去解释的而已。 依赖注入:是从需要实现的业务逻辑上面去解释的,你实现这个逻辑,需要将写好的某个模块,也...

    PHP有很多的设计模式,比如单例模式,订阅模式,策略模式,工厂模式,观察者模式,这些设计模式其实无非都是为了让程序简化,容易维护,模块间解耦。现在我们来讲讲PHP的另外一种设计模式,控制反转/依赖注入,这两者其实是同一个概念,只是凶不同的角度去解释的而已。

    依赖注入:是从需要实现的业务逻辑上面去解释的,你实现这个逻辑,需要将写好的某个模块,也就是你依赖的功能服务者,注入到程序容器中去,然后去使用这个注入的依赖。

    控制反转:是从容器方面来解释的,在你去注入依赖的时候,其实不是业务程序自己去一个一个的去注入的,而是容器将注入进来的依赖反注入到程序中去,这个反注入的含义其实就是执行某个依赖的中的方法(一般都是所有依赖都要约定的方法,比如register),将这个依赖所需要的一些属性,参数等,再次注入到容器中,这个过程就有点反过来的意思,控制反转就是这么来的。
     

    下面我们来看看程序的实现。。。

    比如微信支付的依赖服务

    在使用的时候可以new一下容易缔造类,它会自动去注入这些服务,

     这个时候容器就会调用register方法去注入对应的依赖服务

    然而,容器里面的register方法其实是将注入进来的依赖服务里面的约定好的方法实现, 

     这个依赖服务里面的这个方法其实就是讲服务需要的一些东西注入到容器里面,注意,他传入的是$this,这个$this其实就是容器container 自己本身。这样就实现了控制反转/依赖注入。

    理解有限,有不同的看法,欢迎指正。

    展开全文
  • php class A { public $b; public $c; public function A() { //TODO } public function Method() { $this->b=new B(); $this->c=new C(); $this->b->Method(); $this->c-&...
  • 先看一个例子:<?php class A { public $b; public $c; public function A() { //TODO } public function Method() { $this->b=new B(); $this->c=new C()...
  • https://www.cnblogs.com/sweng/p/6392336.html   先看一个例子: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ... ...
  • 主要帮助大家理解php依赖注入和控制反转,感兴趣的小伙伴们可以参考一下
  • 名词解释:IoC - Inversion of Control 控制反转 DI - Dependency Injection 依赖注入依赖注入和控制反转说的实际上是同一个东西,它们是一种设计模式,这种设计模式用来减少程序间的耦合依赖注入是从应用程序的角度...
  • IOC(inversion of control)控制反转模式;控制反转是将组件间的依赖关系从程序内部提到外部来管理; DI(dependency injection)依赖注入模式;依赖注入是指将组件的依赖通过外部以参数或其他形式注入; 两个说法本质...
  • PHP实现控制反转

    2017-02-25 00:31:45
    Fruit.php <?php /** *@authorGonn,http://www.nowamagic.net/ */ interfaceFruit{ publicfunctionshowColor(); } Apple.php classAppleimplem...
  • 控制反转

    2019-09-19 23:50:32
    依赖注入介绍例子Laravel底层例子 介绍 一种设计模式,通过构造函数或一个方法,把一个外部实例注入进本类内部,而不是在本类内部创建外部实例,就是...src/Share.php中的Share类里面需要使用EasyWeChat\Foundatio...
  • 要想理解 PHP 依赖注入 和 控制反转 两个概念,就必须搞清楚如下的两个问题: DI —— Dependency Injection 依赖注入 IoC —— Inversion of Control 控制反转 什么是依赖注入 没有你我就活不下去,那么,...
  • 依赖注入是一种设计模式,又名 控制反转 ,为了降低耦合度 1:控制反转(Inversion of Control )的实现方式 简称:IOC 理解:A类不需要主动去获取C,而是被动等待,等待IoC/DI的容器获取一个C的实例,然后反向的...
  • 前言最近在使用ThinkPHP5框架,看了下他的源码,发现有很多地方也用到了依赖注入(控制反转),觉得有必要和大家简单聊一聊什么是依赖注入以及怎么使用它。简介IoC - Inversion of Control 控制反转 DI - ...
  • Inversion of control 中文名称:控制反转/依赖注入 通常情况下:调用者来创建被调用者的实例,例如。 class a{} class b{ public function t(){ $c = new a(); ...
  • 依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程序间的耦合,先别追究这个设计模式的定义,否则你一定会被说的云里雾里,下面就以PHP的角度来描述一下依赖注入这个概念。先假设我们...
  • 要想理解php依赖注入和控制反转两个概念,就必须搞清楚如下的问题: DI——Dependency Injection 依赖注入 IoC——Inversion of Control 控制反转 1、参与者都有谁?  答:一般有三方参与者,一个是某个对象;...
  • 要想理解 PHP依赖注入和控制反转两个概念,就必须搞清楚如下的两个问题: DI—— Dependency Injection 依赖注入 IoC—— Inversion of Control 控制反转 什么是依赖注入 没有你我就活不下去,那么,你就是...

空空如也

空空如也

1 2 3 4 5 ... 14
收藏数 279
精华内容 111
关键字:

php控制反转