浅谈依赖注入与控制反转

CodeNebula
• 阅读 4863

前言:设计模式其实是一个很空洞的东西,设计模式有几十种,有些人觉得工厂模式也单例模式已经足够解决大部分问题。而有些人觉得任何设计模式都会让开发变得更“复杂”,更“低效”。所以千万不要太过追求他的实际意义和作用,否则你已经坠入云雾。但是不管怎么样,实际工作中还是要对它们有所了解,下面从php的角度来讲一下依赖注入、控制反转、反射等概念。如有错误之处,还望路过大神多加指点

首先设定场景,假如一个类需要数据库连接,最简单的做法可能是:

class example {
        private $_db;
        function __construct(){
            include "./Lib/Db.php";
            $this->_db = new Db("localhost","root","123456","test");
        }
        function getList(){
            $this->_db->query("......");
        }
    }

但事实上稍微有点经验的同学都不会这样写,因为一旦越来越多的类用到db,而db一旦发生变化,那么岂不是要每个文件都修改一遍?这就是程序设计中的耦合问题。所有的类过度依赖 "./Lib/Db.php" 这个文件。OK,为了解决这个问题,工厂模式出现了,我们新建一个 Factory 工厂类:

class Factory {
        public static function getDb(){
            include "./Lib/Db.php";
            return new Db("localhost","root","123456","test");
        }
    }
    class example {
        private $_db;
        function __construct(){
            $this->_db = Factory::getDb();
        }
        function getList(){
            $this->_db->query("......");
        }
    }

如果我们用到db模块那么直接 Factory::getDb() 从工厂中取出来就是了,看似解决了问题。但事实是这样吗?不,这样只不过是把程序与 db 模块的耦合转移到了 Factory ,一旦后期业务发生变动,Factory 发生变动,依旧要对每个文件改动。那如何解决呢?

我们可以不从example类内部获取db组件,我们从外部把db组件注入进example类

class example {
        private $_db;
        function getList(){
            $this->_db->query("......");//执行查询
        }
        //从外部注入db连接
        function setDb($connection){
            $this->_db = $connection;
        }
    }
    $example = new example();
    $example->setDb(Factory::getDb());//注入db连接
    $example->getList();

这样一来example就不用关心db组件怎么来的了,只用暴露一个注入方法即可。这就是 DI/依赖注入(Dependency Injection) , 不在内部处理依赖关系,而是把依赖作为参数传递进去,以降低程序的耦合性 。

然后我们的项目继续进行,用到了文件处理类,图像处理类,我们可能会这样写

$example->setDb(Factory::getDb());//注入db连接
$example->setFile(Factory::getFile());//注入文件处理类
$example->setImage(Factory::getImage());//注入Image处理类

但是这样似乎也不行啊,每次都要写这么多代码,于是我们又写了一个工厂方法

class Factory {
        public static function getExample(){
            $example = new example();
            $example->setDb(Factory::getDb());//注入db连接
            $example->setFile(Factory::getFile());//注入文件处理类
            $example->setImage(Factory::getImage());//注入Image处理类
            return $expample;
        }
    }

example也不直接new 了。我们用 Factory::getExample()中获取。但是,这是不是又有点熟悉的感觉?和上面第一次用工厂类的时候一样依赖于工厂。于是又有了容器的概念。

class example {
    private $_di;
    function __construct(Di &$di){
        $this->_di = $di;
    }
    //通过di容器获取db实例
    function getList(){
        $this->_di->get('db')->query("......");
    }
}
$di = new Di();
$di->set("db",function(){
   return new Db("localhost","root","root","test"); 
});
$example = new example($di);
$example->getList();

Di就是一个存放各种可扩展的组件的容器,需要注入的时候调用$di->set()方法注入组件即可。程序中即可通过$di->get() 获取组件。这样被调用的组件(db)并不是由调用者(example)创建,而是由Di容器创建, 调用者失去控制权,而容器得到控制权,发生了控制权转移 ,所以叫 做控制反转(Inversion of Control)

但是这样又有一些比较有强迫症的同学发现了,每个类都要注入一遍容器是不是有些麻烦。没错,其实注入容器这个动作可以交给另外的程序处理,那就是反射。

<?php
/** 
* example
*/
class example {
    //通过di容器获取db实例
    function getList(){
        $this->_di->get('db')->query("......");
    }
}
//di容器
class Di{
    public $_container;
    public function get($cls){
        return $this->_container[$cls];
    }
    public function set($cls,$_instance){
        $this->_container[$cls]=$_instance;
    }
}
//db组件
class db{
    private static $_instance;//保存单例
    //单例方法
    public static function getInstance(){
        if(!(self::$_instance instanceof self)){
            self::$_instance = new self;
        }
        return self::$_instance;
    }
    //查询方法
    public function query($sql){
        echo $sql;
    }
}

$di = new Di();//实例化容器
$di->set('db',db::getInstance());   //注入db实例


$reflector = new ReflectionClass('example');    //反射example,通过反射可以获得该类的所有信息
$reflector->getDefaultProperties();             //example属性
$reflector->getDocComment();               //注释

$instance =$reflector->newInstanceArgs();       //相当于实例化反射的example类
$instance->_di=$di;                             //注入di容器

$reflector->getmethod('getList')->invoke($instance);//调用example类方法

通过反射我们可以得到该类的全部信息,包括方法、方法名、属性甚至注释等等。通过反射我们可以方便的控制程序中使用的类,对他们进行扩展、修正、以及监听。通常反射在插件开发和框架开发中大量应用。在框架开发中也会把反射与依赖注入、控制反转搭配使用,让程序有强大的可控性和扩展性。

博客链接:浅谈依赖注入与控制反转

点赞
收藏
评论区
推荐文章
3A网络 3A网络
2年前
Golang 常见设计模式之单例模式
之前我们已经看过了Golang常见设计模式中的装饰和选项模式,今天要看的是Golang设计模式里最简单的单例模式。单例模式的作用是确保无论对象被实例化多少次,全局都只有一个实例存在。根据这一特性,我们可以将其应用到全局唯一性配置、数据库连接对象、文件访问对象等。Go语言实现单例模式的方法有很多种,下面我们就一起来看一下。饿汉式饿汉式实现单例模式非
Wesley13 Wesley13
3年前
java中饿汉与懒汉的故事(单例设计模式)
java中的单例设计模式关于设计模式,这其实是单独存在的东西,它不属于java,但是在java中使用较多,所以今天我就给大家介绍下单例设计模式中的饿汉和懒汉这俩朴素的打工人。首先我先说明下单例设计模式是啥(如果不想了解,可以直接划下去看饿汉和懒汉):类的单例设计模式就是采用一定的方法保证在整个软件系统中,对某个类只能存在一
九路 九路
4年前
Java实现 一篇文章说尽设计模式之六大原则
我们知道,设计模式很有用,学好设计模式不但能让你写出更简洁,优雅的代码,还能使得代码的结构更清晰,也更有利于扩展当然设计模式也不是万能的,一成不变的.设计模式只是前人总结出来的一种经验,一种特定问题的解决方法,不能看作是死的东西不一定非要生搬硬套,非得按照设计模式书上来来,只要我们写的代码符合一定的一些原则,一样可以看作是自己的模式.但是前人总结
Wesley13 Wesley13
3年前
java24种设计模式
一、设计模式定义  设计模式(DesignPattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性。二、设计模式分类  经典模式只有23个(还有简单工厂模式),它们各具特色,每个模式都为某一个可重复的设计问题提供了一套解决方案。  根据它们的用
Wesley13 Wesley13
3年前
(面试常问)4种单例设计模式的总结(内含代码以及分析)
单例设计模式:  单例模式,是一种常见的软件设计模式.在它的核心结构中只包含了一个被称为单例的特殊类.通过单例模式可以保证系统中只有该类的一个实例对象.优点:  实例控制:单例模式会阻止其它对象实例化其自己的单例对象的副本,从而确保所有对象都访问的是唯一的实例   灵活性:因为类控制了实例化过程,所以类可以很灵活的更改实
Wesley13 Wesley13
3年前
Java面试题总结
1)用过哪些设计模式,手写一个(除单例); 工厂模式,门面模式(个人有点像service层),代理模式2)springmvc的核心是什么,请求的流程是怎么处理的,控制反转怎么实现的; 1,用户发送请求至前端控制器DispatcherServlet 2、DispatcherServlet收到请求调用HandlerMapping处理器映射器
Wesley13 Wesley13
3年前
23种设计模式(1):单例模式
定义:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。类型:创建类模式类图:!23种设计模式(1):单例模式第1张|快课网(http://static.oschina.net/uploads/img/201407/05200605_0dij.gif"23种设计模式(1):单例模式
Wesley13 Wesley13
3年前
Java中23种设计模式详解
Java中23种设计模式1\.设计模式31.1创建型模式41.1.1工厂方法41.1.2抽象工厂61.1.3建造者模式101.1.4单态模式131.1.5原型模式151.2结构型模式171.2.1适配器模式171.2.2桥接模式191.2.3组合
Wesley13 Wesley13
3年前
JavaWeb之动态代理解决request请求编码问题
动态代理解决编码问题1.设计模式出现原因:软件开发过程中,遇到相似问题,将问题的解决方法抽取模型(套路)常见设计模式:单例,工厂,适配器,装饰者,动态代理。2.装饰者模式简单介绍谷歌汽车开发场景1.Java定义了汽车开发约定interfaceICar{s
前端常用设计模式初探 | 京东云技术团队
设计模式一直是程序员谈论的“高端”话题之一,总有一种敬而远之的心态。在了解后才知道在将函数作为一等对象的语言中,有许多需要利用对象多态性的设计模式,比如单例模式、策略模式等,这些模式的结构与传统面向对象语言的结构大相径庭,实际上已经融入到了语言之中,我们可
京东云开发者 京东云开发者
5个月前
设计模式之代理模式:武器附魔之道
作者:京东保险孙昊宇大家好,今天我们聊聊设计模式中的代理模式。作为一种经典设计模式,它的应用极为广泛。不论你是刚刚入门,还是已经熟悉设计模式,相信这篇文章都会让你有所收获。一、引子:叫个代驾让我们从一个引子开始:司机和代驾。「私家车司机」和「代驾」是什么关