前言:设计模式实际上是一个很空洞的东西,设计模式有几十种,有些人以为工厂模式也单例模式已经足够解决大部分问题。而有些人以为任何设计模式都会让开发变得更“复杂”,更“低效”。因此千万不要太过追求他的实际意义和做用,不然你已经坠入云雾。可是无论怎么样,实际工做中仍是要对它们有所了解,下面从php的角度来说一下依赖注入、控制反转、反射等概念。若有错误之处,还望路过大神多加指点php
首先设定场景,假如一个类须要数据库链接,最简单的作法多是:html
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 工厂类:sql
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) , 不在内部处理依赖关系,而是把依赖做为参数传递进去,以下降程序的耦合性 。框架
而后咱们的项目继续进行,用到了文件处理类,图像处理类,咱们可能会这样写this
$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)code
可是这样又有一些比较有强迫症的同窗发现了,每一个类都要注入一遍容器是否是有些麻烦。没错,其实注入容器这个动做能够交给另外的程序处理,那就是反射。
<?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类方法
经过反射咱们能够获得该类的所有信息,包括方法、方法名、属性甚至注释等等。经过反射咱们能够方便的控制程序中使用的类,对他们进行扩展、修正、以及监听。一般反射在插件开发和框架开发中大量应用。在框架开发中也会把反射与依赖注入、控制反转搭配使用,让程序有强大的可控性和扩展性。
博客连接:浅谈依赖注入与控制反转