依賴(lài)注入和控制反轉(zhuǎn)在現(xiàn)如今的主流框架中經(jīng)常可以看到,主要是用于解決程序的松耦合,便于程序的協(xié)調(diào)開(kāi)發(fā)和管理。
DI——Dependency Injection 依賴(lài)注入
IoC——Inversion of Control 控制反轉(zhuǎn)
當(dāng)一個(gè)類(lèi)的實(shí)例需要另一個(gè)類(lèi)的實(shí)例協(xié)助時(shí),在傳統(tǒng)的程序設(shè)計(jì)過(guò)程中,通常由調(diào)用者來(lái)創(chuàng)建被調(diào)用者的實(shí)例。而采用依賴(lài)注入的方式,創(chuàng)建被調(diào)用者的工作不再由調(diào)用者來(lái)完成,因此叫控制反轉(zhuǎn),創(chuàng)建被調(diào)用者的實(shí)例的工作由IOC容器來(lái)完成,然后注入調(diào)用者,因此也稱(chēng)為依賴(lài)注入。
控制反轉(zhuǎn)(Inversion of Control):當(dāng)調(diào)用者需要被調(diào)用者的協(xié)助時(shí),在傳統(tǒng)的程序設(shè)計(jì)過(guò)程中,通常由調(diào)用者來(lái)創(chuàng)建被調(diào)用者的實(shí)例,但在這里,創(chuàng)建被調(diào)用者的工作不再由調(diào)用者來(lái)完成,而是將被調(diào)用者的創(chuàng)建移到調(diào)用者的外部,從而反轉(zhuǎn)被調(diào)用者的創(chuàng)建,消除了調(diào)用者對(duì)被調(diào)用者創(chuàng)建的控制,因此稱(chēng)為控制反轉(zhuǎn)。
依賴(lài)注入(Dependency Injection):要實(shí)現(xiàn)控制反轉(zhuǎn),解決方案是將創(chuàng)建被調(diào)用者實(shí)例的工作交由IoC容器來(lái)完成,然后在調(diào)用者中注入被調(diào)用者(通過(guò)構(gòu)造器/方法注入實(shí)現(xiàn)),這樣我們就實(shí)現(xiàn)了調(diào)用者與被調(diào)用者的解耦,該過(guò)程被稱(chēng)為依賴(lài)注入。依賴(lài)注入是控制反轉(zhuǎn)的一種實(shí)現(xiàn)方式。常見(jiàn)注入方式有三種:setter、constructor injection、property injection。
比如我們寫(xiě)一個(gè)file的緩存類(lèi),類(lèi)user需要用到這個(gè)緩存類(lèi) 傳統(tǒng)的調(diào)用方式是在調(diào)用者中實(shí)例化被調(diào)用者
class fileCache{
/**
* 設(shè)置緩存
* @param $name
* @param $value
*/
public function set($name ,$value){
//TODO
}
/**
* 刪除緩存
* @param $name
*/
public function delete($name){
//TODO
}
/**
* 獲取緩存數(shù)據(jù)
* @param $name
*/
public function get($name){
//TODO
}
}
class User {
protected $cache;
public function __construct() {
$this->cache = new fileCache();
}
public function getUser(){
return $this->cache->get("user");
}
}
$user = new User();
$user->getUser();
一旦平臺(tái)或者環(huán)境發(fā)生變化,我們不用file來(lái)做為緩存,而是用redis做緩存,那相應(yīng)調(diào)用類(lèi)User就需要做出修改。程序不應(yīng)該依賴(lài)于具體的實(shí)現(xiàn),而是要依賴(lài)抽像的接口
class fileCache implements Cache{
public function set($name)
{
//TODO
}
public function get($name)
{
//TODO
}
public function delete($name)
{
//TODO
}
}
class redisCache implements Cache{
public function set($name)
{
//TODO
}
public function get($name)
{
//TODO
}
public function delete($name)
{
//TODO
}
}
構(gòu)造器注入
class User{
protected $cache ;
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
public function getUser()
{
return $this->cache->get('user');
}
}
$redis = new redisCache();
$user = new User($redis);
$user->getUser();
$file = new fileCache();
$user = new User($file);
$user->getUser();
setter注入
class User{
protected $cache ;
public function setCache( Cache $cache)
{
$this->cache = $cache;
}
public function getUser()
{
return $this->cache->get('user');
}
}
$redis = new redisCache();
$user = new User();
$user->setCache($redis);
$user->getUser();
$file = new fileCache();
$user = new User();
$user->setCache($file);
$user->getUser();
但是這樣的注入還會(huì)產(chǎn)生一個(gè)問(wèn)題,當(dāng)調(diào)用類(lèi)依賴(lài)于多個(gè)外部類(lèi)時(shí),我們就需要不斷的寫(xiě)set
$user = new User();
$user->setDb($db);//注入db連接
$user->setCache($file);//注入緩存處理類(lèi)
#.....
如果引入的外部類(lèi)很多時(shí),就會(huì)變得十分繁瑣。這就引入另一個(gè)概念容器又叫做IoC容器、DI容器。
這里我們引入一個(gè)約定:在User類(lèi)的構(gòu)造函數(shù)里傳入一個(gè)名為Di $di的參數(shù),如下:
class Di{
protected $_objects = [];
public function set($name, $object)
{
$this->_objects[$name] = $object;
}
public function get($name) {
return $this->_objects[$name];
}
}
class User {
private $_di;
function __construct(Di &$di)
{
$this->_di = $di;
}
//通過(guò)di容器獲取db實(shí)例
public function getUser()
{
return $this->_di->get('cache')->get('User');
}
}
$di = new Di();
$cache = new fileCache();
$di->set("cache",$cache);
$user = new User($di);
$user->getUser();