PHP三層結構(下) PHP實現AOP第2/2頁
更新時間:2010年07月04日 22:18:58 作者:
讓我們把注意力集中到中間服務層上來。中間服務層代碼比較簡單,只是調用數據訪問層代碼將留言保存到數據庫。
不過先不要忙著高興,有個問題就在這個可擴展性里。當新的需求被提出之后,我們可以再添加 ILWordExtension 實現類,這個的確正確。但是將這個新類注冊到getExtArray函數里,等于說還是要修改主業(yè)務邏輯代碼。能不能不修改呢?每次有新的需求變化還是要告知主業(yè)務邏輯,這樣終歸不太好。最理想的情況是新的擴展代碼加入系統(tǒng)之后,主業(yè)務邏輯代碼不用修改,因為主業(yè)務邏輯根本不知道有新擴展這回事!為此我們還需要優(yōu)化一下設計方案,如圖2所示:

(圖2),加入擴展家族類
對于調用擴展的主程序(也就是中間服務類LWordServiceCore),只讓它知道有ILWordExtension(擴展)這件事就可以了,它不需要知道還有CheckPowerExtension(檢查權限擴展)、CheckContentExtension(檢查內容擴展)和AddScoreExtension(加分擴展)這三個類。對這三個類的調用過程被移動到LWordExtensionFamily (擴展家族類)里去了。
LWordExtensionFamily其實就是一個能存放多個ILWordExtension接口實例的容器類,從圖2中可以看出這個容器類不僅僅是實現了ILWordExtension接口,而且還聚合多個ILWordExtension接口的實例,所以它很特殊!對于LWordServiceCore類,這個類只知道ILWordExtension接口,但并不知道這個接口存在三個實現類。恰好LWordExtensionFamily類就實現了ILWordExtension接口,這很好的符合了中間服務類的要求,并且這個擴展家族類知道ILWordExtension存在三個實現類,并會一一調用它們, LWordExtensionFamily代碼大概如代碼8所示:
復制代碼 代碼如下:
// 代碼 8, 擴展家族
// 擴展家族
class LWordExtensionFamily implements ILWordExtension {
// 擴展數組
private $_extensionArray = array();
// 添加擴展
public function addExtension(ILWordExtension $extension) {
$this->_extensionArray []= $extension;
}
// 添加留言前
public function beforeAppend($newLWord) {
foreach ($this->_extensionArray as $extension) {
$extension->beforeAppend($newLWord);
}
}
// 添加留言后
public function behindAppend($newLWord) {
foreach ($this->_extensionArray as $extension) {
$extension->behindAppend($newLWord);
}
}
}
通過代碼8不難看出LWordExtensionFamily類雖然也實現了ILWordExtension接口,但是它并不做任何實質的操作,而是通過循環(huán)語句將調用過程一一傳遞下去。為了平滑實現擴展到插入的方式,所以最好創(chuàng)建一個工廠類MyExtensionFactory。如代碼9所示:
復制代碼 代碼如下:
// 代碼 9
// 自定義擴展工廠
class MyExtensionFactory {
// 創(chuàng)建留言擴展
public static function createLWordExtension() {
$lwef = new LWordExtensionFamily();
// 添加擴展
$lwef->addExtension(new CheckPowerExtension());
$lwef->addExtension(new CheckContentExtension());
$lwef->addExtension(new AddScoreExtension());
return $lwef;
// 注意這里返回的是擴展家族類對象,
// 擴展家族 LWordExtensionFamily 恰好也實現了接口 ILWordExtension,
// 所以這是符合業(yè)務邏輯的要求.
// 從此, 業(yè)務邏輯可以不關心具體的擴展對象, 只要知道擴展家族即可
}
}
使用擴展工廠類的好處就是可以隨意的添加和移除擴展實例,這就很好的實現了可插入式編程。對于LWordServiceCore類只知道一個ILWordExtension接口,對于LWordExtensionFamily知道需要一一調用每個擴展,但是具體會有多少個擴展是通過MyExtensionFactory給出的。各負其責結構也很清晰。如果我們做一個假設,MyExtensionFactory類的createLWordExtension函數不是通過new關鍵字這樣的硬編碼方式來添加擴展列表,而是通過更巧妙的讀取配置文件的方式來得到擴展列表,那么是不是更方便更靈活呢?不過這個就不再本文中討論了。
中間服務層通過工廠類取得一個ILWordExtension接口的具體實例,然后調用其beforeAppend和behindAppend方法。當然中間服務并不知道工廠類返回的其實是一個含有多個ILWordExtension實例的容器(因為這個容器也實現了ILWordExtension接口),所以中間服務也就不知道擴展是被一一調用的。完整代碼如代碼10所示:
復制代碼 代碼如下:
// 代碼 10, 完整代碼
// 擴展接口
interface ILWordExtension {
// 添加留言前
public function beforeAppend($newLWord);
// 添加留言后
public function behindAppend($newLWord);
};
// 檢查權限
class CheckPowerExtension implements ILWordExtension {
// 添加留言前
public function beforeAppend($newLWord) {
// 在這里判斷用戶權限
}
// 添加留言后
public function behindAppend($newLWord) {
}
};
// 檢查留言文本
class CheckContentExtension implements ILWordExtension {
// 添加留言前
public function beforeAppend($newLWord) {
if (stristr($newLWord, "fuck"))
throw new Exception();
}
// 添加留言后
public function behindAppend($newLWord) {
}
};
// 用戶積分
class AddScoreExtension implements ILWordExtension {
// 添加留言前
public function beforeAppend($newLWord) {
}
// 添加留言后
public function behindAppend($newLWord) {
// 在這里給用戶積分
}
};
// 擴展家族
class LWordExtensionFamily implements ILWordExtension {
// 擴展數組
private $_extensionArray = array();
// 添加擴展
public function addExtension(ILWordExtension $extension) {
$this->_extensionArray []= $extension;
}
// 添加留言前
public function beforeAppend($newLWord) {
foreach ($this->_extensionArray as $extension) {
$extension->beforeAppend($newLWord);
}
}
// 添加留言后
public function behindAppend($newLWord) {
foreach ($this->_extensionArray as $extension) {
$extension->behindAppend($newLWord);
}
}
}
// 自定義擴展工廠
class MyExtensionFactory {
// 創(chuàng)建留言擴展
public static function createLWordExtension() {
$lwef = new LWordExtensionFamily();
// 添加擴展
$lwef->addExtension(new CheckPowerExtension());
$lwef->addExtension(new CheckLWordExtension());
$lwef->addExtension(new AddScoreExtension());
return $lwef;
}
}
// 中間服務層
class LWordServiceCore implements ILWordService {
// 添加留言
public function append($newLWord) {
// 獲取擴展
$ext = MyExtensionFactory::createLWordExtension();
$ext->beforeAppend($newLWord);
// 調用數據訪問層
$dbTask = new LWordDBTask();
$dbTask->append($newLWord);
$ext->behindAppend($newLWord);
}
};
從代碼10中可以看出雖然CheckPowerExtension、CheckContentExtension、AddScoreExtension以及LWordExtensionFamily都實現了ILWordExtension接口,但是它們的beforeAppend和behindAppend函數過程卻完全不同!特別是LWordExtensionFamily擴展家族類,它并沒有實質的業(yè)務邏輯處理過程,而是將調用依次傳遞給每一個擴展。beforeAppend和behindAppend函數在具體類中的不同實現,這是面向對象程序設計中的很典型的特性:多態(tài)!
將次業(yè)務邏輯分散到各個擴展中,這種做法已經非常近似AOP(Aspect OrientedProgramming,面向切面編程)的編程方式。權限校驗、內容檢查和積分可以看作是不同的切面,這些切面和主業(yè)務邏輯交叉在一起,但又不會影響到主業(yè)務邏……這樣做的好處就是擴展代碼不會干擾主業(yè)務邏輯,我們也可以針對某一個擴展進行編碼和單元測試,然后通過MyExtensionFactory工廠類把擴展插入到業(yè)務流程中。完整的執(zhí)行過程如圖3所示:

(圖3),執(zhí)行流程
本文源碼下載地址:http://xiazai.jb51.net/201007/yuanma/TraceLWord.rar
相關文章
php 備份數據庫代碼(生成word,excel,json,xml,sql)
本篇文章是對php備份數據庫代碼(生成word,excel,json,xml,sql)進行了詳細的分析介紹,需要的朋友參考下2013-06-06php select,radio和checkbox默認選擇的實現方法
radio和checkbox默認選擇的實現方法,大家參考下原理就知道了,不論asp,asp.net,jsp都是這個原理。2010-05-05如何在舊的PHP系統(tǒng)中使用PHP 5.3之后的庫
這篇文章主要介紹了如何在舊的PHP系統(tǒng)中使用PHP 5.3之后的庫,需要的朋友可以參考下2015-12-12php+jQuery遞歸調用POST循環(huán)請求示例
這篇文章主要介紹了php+jQuery遞歸調用POST循環(huán)請求,結合實例形式分析了php+jQuery的ajax方法遞歸調用與json轉換技巧,需要的朋友可以參考下2016-10-10PHP自定義遞歸函數實現數組轉JSON功能【支持GBK編碼】
這篇文章主要介紹了PHP自定義遞歸函數實現數組轉JSON功能,針對json_encode函數處理GBK編碼中文出現亂碼的情況,使用自定義函數進行數組遞歸遍歷實現可兼容GBK編碼的數組轉json功能,需要的朋友可以參考下2018-07-07php數組函數序列 之array_count_values() 統(tǒng)計數組中所有值出現的次數函數
array_count_values() 函數用于統(tǒng)計數組中所有值出現的次數,本函數返回一個數組,其元素的鍵名是原數組的值,鍵值是該值在原數組中出現的次數。2011-10-10