依賴注入和控制反轉在現如今的主流框架中經常可以看到,主要是用于解決程序的松耦合,便于程序的協調開發和管理。
DI——Dependency Injection 依賴注入
IoC——Inversion of Control 控制反轉
當一個類的實例需要另一個類的實例協助時,在傳統的程序設計過程中,通常由調用者來創建被調用者的實例。而采用依賴注入的方式,創建被調用者的工作不再由調用者來完成,因此叫控制反轉,創建被調用者的實例的工作由IOC容器來完成,然后注入調用者,因此也稱為依賴注入。
控制反轉(Inversion of Control):當調用者需要被調用者的協助時,在傳統的程序設計過程中,通常由調用者來創建被調用者的實例,但在這里,創建被調用者的工作不再由調用者來完成,而是將被調用者的創建移到調用者的外部,從而反轉被調用者的創建,消除了調用者對被調用者創建的控制,因此稱為控制反轉。
依賴注入(Dependency Injection):要實現控制反轉,解決方案是將創建被調用者實例的工作交由IoC容器來完成,然后在調用者中注入被調用者(通過構造器/方法注入實現),這樣我們就實現了調用者與被調用者的解耦,該過程被稱為依賴注入。依賴注入是控制反轉的一種實現方式。常見注入方式有三種:setter、constructor injection、property injection。
比如我們寫一個file的緩存類,類user需要用到這個緩存類 傳統的調用方式是在調用者中實例化被調用者
class fileCache{
/**
* 設置緩存
* @param $name
* @param $value
*/
public function set($name ,$value){
//TODO
}
/**
* 刪除緩存
* @param $name
*/
public function delete($name){
//TODO
}
/**
* 獲取緩存數據
* @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();
一旦平臺或者環境發生變化,我們不用file來做為緩存,而是用redis做緩存,那相應調用類User就需要做出修改。程序不應該依賴于具體的實現,而是要依賴抽像的接口
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
}
}
構造器注入
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();
但是這樣的注入還會產生一個問題,當調用類依賴于多個外部類時,我們就需要不斷的寫set
$user = new User();
$user->setDb($db);//注入db連接
$user->setCache($file);//注入緩存處理類
#.....
如果引入的外部類很多時,就會變得十分繁瑣。這就引入另一個概念容器又叫做IoC容器、DI容器。
這里我們引入一個約定:在User類的構造函數里傳入一個名為Di $di的參數,如下:
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;
}
//通過di容器獲取db實例
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();