1. 名詞介紹
OOD,物件導向設計
DIP,依賴倒置(軟體設計原則)
IOC,控制反轉(軟體設計模式)
DI,依賴注入
IOC Container,控制反轉容器,也是依賴注入容器
2. 組成部分
服務清單(功能清單,service list)
服務(高層類,service ,對外提供服務)
服務提供者(底層類,service provider ,實際提供服務的物件)
2. 依賴倒置原則(DIP)
2.0 介紹
依賴倒置原則,它轉換了依賴,高層模組不依賴於低層模組的實現,而低層模組依賴於高層模組定義的介面
2.1 場景描述
提供一個計算機儲存的服務。需要根據不同的使用者需求,使用不同的儲存裝置。
2.2 沒有遵循依賴倒置原則的例子
2.2.1 定義好服務提供者(實際提供服務)
// 定義一個 硬碟儲存類 (服務提供者)
class HardDiskStorage {
public function saveToHardDisk(){
}
public function readFromHardDisk(){
}
}
// 定義一個 U盤儲存類(服務提供者)
class UStorage {
public function saveToU(){
}
public function readFromU(){
}
}
2.2.2 定義 服務(對外提供服務的物件)
/**
* 定義一個 ComputerStorage 類(儲存服務)
*/
// 第一種:使用硬碟作為提供實際服務的物件
class ComputerStorage {
protected $_storage = null;
function __construct(){
$this->_storage = new HardDiskStorage();
}
public function save(){
$this->_storage->saveToHardDisk();
}
public function read(){
$this->_storage->readFromHardDisk();
}
}
// 第二種:使用 U 盤作為提供實際服務的物件
class ComputerStorage {
protected $_storage = null;
function __construct(){
$this->_storage = new UStorage();
}
public function save(){
$this->_storage->saveToU();
}
public function read(){
$this->_storage->readFromU();
}
}
// 讀取
$cs = new ComputerStorage();
$cs->read();
2.2.3 程式碼分析
根據上面的程式碼,當切換服務提供者時,服務類的程式碼需要做較多的改動。服務(ComputerStorage
)本省作為一個高層類,對外提供訪問,卻受制於提供具體服務的服務提供者(HardDiskStorage
、UStorage
)定義的實現(saveToHardDisk
、saveToU
、readFromHardDisk
、readFromU
),高層模組依賴底層模組實現,違背了依賴倒置原則。
2.3 遵循依賴倒置原則的例子
2.3.1 場景
同 2.1
介紹中場景。
2.3.2 定義服務清單(高層模組定義介面)
interface ServiceList {
public function save();
public function read();
}
2.3.3 定義服務提供者
// 硬碟
class HardDiskStorage implements ServiceList {
public function save(){
}
public function read(){
}
}
// U 盤
class UStorage implements ServiceList {
public function save(){
}
public function read(){
}
}
2.3.4 定義服務
class ComputerStorage {
protected $_storage = null;
function __construct(){
$this->_storage = new HardDiskStorage();
}
public function save(){
$this->_storage->save();
}
public function read(){
$this->_storage->read();
}
}
$cs = new ComputerStorage();
$cs->read();
2.3.5 程式碼分析
上述程式碼中,事先定義了好了服務清單(介面,ServiceList
),然後服務提供者實現這些介面(HardDiskStorage
、UStorage
),服務(ComputerStorage
)只需要切換服務提供者即可(HardDiskStorage
、UStorage
),完全無需理會他們的實現(readFromHardDisk
、readFromU
…等)。高層模組不依賴於底層模組定義的實現,遵循了依賴倒置原則
3. 控制反轉(IOC) + 依賴注入(DI)
3.0 介紹
控制反轉(IoC),它為相互依賴的元件提供抽象,將依賴(低層模組)物件的獲得交給第三方(系統)來控制,即依賴物件不在被依賴模組的類中直接通過new來獲取
3.1 場景
同 2
場景
3.2 沒有實現控制反轉的例子
2
中的例子就是沒有實現控制反轉的例子。2
中 ComputerStorage
獲取依賴(HardDiskStorage
或 UStorage
)的途徑都是在 contruct
建構函式中獲取的,即 類內部例項化依賴獲取。
3.3 實現控制反轉的例子
以下程式碼是根據 2
中的程式碼做了些許的調整。
class ComputerStorage {
protected $_storage = null;
/**
* 內部只獲取依賴的例項
*/
public function setStorage($storage){
$this->_storage = $storage;
}
public function save(){
$this->_storage->save();
}
public function read(){
$this->_storage->read();
}
}
// 外部例項化依賴
$hardDiskStorage = new HardDiskStorage();
$cs = new ComputerStorage();
// 注入依賴
$cs->setStorage($hardDiskStorage);
4. 依賴注入容器(IOC 容器)
4.0 場景
同 2
場景。
4.1 使用 IOC容器
class Container {
// 登錄檔
protected static $_registry = null;
// 儲存到登錄檔
public static function set($classname , Callable $create){
self::$_registry[$classname] = $create;
}
// 獲取登錄檔對應類的例項
public static function get($key){
call_user_func(self::$_registry[$key]);
}
}
class ComputerStorage {
protected $_storage = null;
function __construct(){
$this->_storage = Container::get(`HardDiskStorage`);
}
public function read(){
$this->_storage->read();
}
public function save(){
$this->_storage->save();
}
}
/**
* 註冊依賴
*/
Container::set(`HardDiskStorage` , function(){
return new HardDiskStorage();
});
Container::set(`UStorage` , function(){
return new UStorage();
});
// 測試
$cs = new ComputerStorage();
$cs->read();