本文源碼下載地址:http://xiazai.jb51.net/201007/yuanma/TraceLWord.rar
開發環境為 eclipse(pdt)
讓我們把注意力集中到中間服務層上來。中間服務層代碼比較簡單,只是調用數據訪問層代碼將留言保存到數據庫。如代碼1所示:
復制代碼 代碼如下:
// 代碼 1
// 中間服務層
class LWordServiceCore implements ILWordService {
// 添加留言
public function append($newLWord) {
// 調用數據訪問層
$dbTask = new LWordDBTask();
$dbTask->append($newLWord);
}
};
在看到留言板的演示之後,公司的產品部和市場部或許會提出各種各樣的想法和需求。比如他們希望在添加留言之前判斷用戶的權限!只有注冊用戶才能留言!我們需要修改代碼,如代碼2所示:
復制代碼 代碼如下:
// 代碼 2, 增加登錄驗證
// 中間服務層
class LWordServiceCore implements ILWordService {
// 添加留言
public function append($newLWord) {
if (!($userLogin)) {
// 提示用戶登錄
}
// 調用數據訪問層
$dbTask = new LWordDBTask();
$dbTask->append($newLWord);
}
};
市場部又希望在添加留言之前,對留言內容進行檢查,如果留言中含有髒話就不保存。我們繼續修改代碼,如代碼3所示:
復制代碼 代碼如下:
// 代碼 3, 增加髒話過濾
// 中間服務層
class LWordServiceCore implements ILWordService {
// 添加留言
public function append($newLWord) {
if (!($userLogin)) {
// 提示用戶登錄
}
if (stristr($newLWord, "SB")) {
// 含有髒話, 提示留言發送失敗
}
// 調用數據訪問層
$dbTask = new LWordDBTask();
$dbTask->append($newLWord);
}
};
產品部也提出了新需求,他們希望加入積分機制。具體來講就是在用戶每次留言成功以後給用戶+5分。我們繼續修改代碼,如代碼4所示:
復制代碼 代碼如下:
// 代碼 4, 加入留言積分機制
// 中間服務層
class LWordServiceCore implements ILWordService {
// 添加留言
public function append($newLWord) {
if (!($userLogin)) {
// 提示用戶登錄
}
if (stristr($newLWord, "SB")) {
// 含有髒話, 提示留言發送失敗
}
// 調用數據訪問層
$dbTask = new LWordDBTask();
$dbTask->append($newLWord);
// 給用戶加分
$score = getUserScore($userName);
$score = $score + 5;
saveUserScore($userName, $score);
}
};
沒過多久,產品部又對需求進行細化,他們希望用戶積分每積累夠1000分以後,就給用戶升級。我們繼續修改代碼,如代碼5所示:
復制代碼 代碼如下:
// 代碼 5, 加入用戶升級規則
// 中間服務層
class LWordServiceCore implements ILWordService {
// 添加留言
public function append($newLWord) {
if (!($userLogin)) {
// 提示用戶登錄
}
if (stristr($newLWord, "fuck")) {
// 含有髒話, 提示留言發送失敗
}
// 調用數據訪問層
$dbTask = new LWordDBTask();
$dbTask->append($newLWord);
// 給用戶加分
$score = getUserScore($userName);
$score = $score + 5;
saveUserScore($userName, $score);
// 給用戶升級
if (($score % 1000) == 0) {
$level = getUserLevel($userName);
$level = $level + 1;
saveUserLevel($userName, $level);
}
}
};
隨著需求的增多,我們需要不斷的修改中間服務層代碼。但是你應該不難發現,需求越多中間服務層代碼也就越多越龐大!最後會導致即便我們使用三層結構的開發模式,也還是沒有有效的降低工程難度!另外就是應需求的變化而修改中間服務代碼以後,需要重新測試所有代碼,而不是有效的測試新增代碼……
其實讓我們仔細分析一下這個留言板代碼,我先要提出一個主業務邏輯和次業務邏輯的概念。無論怎樣,把留言內容存入到數據庫,這是業務邏輯的主干!這個就是主業務邏輯!這部分沒有隨著需求的增加而修改。至於在存入數據庫之前要進行權限校驗,要進行內容檢查,存入數據庫之後要給用戶加分,然後給用戶升級,這些都是前序工作和掃尾工作,都是次業務邏輯!主業務邏輯幾乎是一成不變的,次業務邏輯變化卻非常頻繁。為了提高代碼的可讀性和可維護性,我們可以考慮把這些次業務邏輯放到別的地方,盡量不要讓它們干擾主業務邏輯。主業務邏輯專心干自己該干的事情好了,至於別的任何事情,主業務邏輯一概都不聞不問!那麼我們的代碼就可以寫成這樣,如代碼6所示:
復制代碼 代碼如下:
// 代碼 6, 將主業務邏輯和次業務邏輯分開
// 中間服務層
class LWordServiceCore implements ILWordService {
// 添加留言
public function append($newLWord) {
// 添加留言前
beforeAppend($newLWord);
// 調用數據訪問層
$dbTask = new LWordDBTask();
$dbTask->append($newLWord);
// 添加留言後
behindAppend($newLWord);
}
};
我們可以把權限判斷代碼和留言內容文本過濾代碼統統塞進beforeAppend函數,把用戶積分代碼塞進behindAppend函數,這樣就把次業務邏輯從主業務邏輯代碼中清理掉了。主業務邏輯知道有個“序曲”函數beforeAppend,有個“尾聲”函數behindAppend,但是在序曲和尾聲函數中具體都做了什麼事情,主業務邏輯並不知道,也不需要知道!當然實際編碼工作並不那麼簡單,我們還要兼顧產品部和市場部更多的需求變化,所以最好能實現一種插件方式來應對這種變化,但是僅僅依靠兩個函數beforeAppend和behindAppend是達不到這個目的~
想要實現插件方式,可以建立接口!使用接口的好處是可以將定義和實現隔離,另外就是實現多態。我們建立一個留言擴展接口ILWordExtension,該接口有兩個函數beforeAppend和behindAppend。權限校驗、內容檢查、加分這些功能可以看作是實現ILWordExtension接口的三個實現類,主業務邏輯就依次遍歷這三個實現類,來完成次業務邏輯。如圖1所示:
CheckPowerExtension擴展類用作用戶權限校驗,CheckContentExtension擴展類用作留言內容檢查,AddScoreExtension擴展類用作給用戶加分和升級。示意代碼如代碼7所示:
(圖1),加入擴展接口
復制代碼 代碼如下:
// 代碼 7,加入擴展接口
// 擴展接口
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, "SB")) {
throw new Exception();
}
}
// 添加留言後
public function behindAppend($newLWord) {
}
};
// 用戶積分
class AddScoreExtension implements ILWordExtension {
// 添加留言前
public function beforeAppend($newLWord) {
}
// 添加留言後
public function behindAppend($newLWord) {
// 在這裡給用戶積分
}
};
// 中間服務層
class LWordServiceCore implements ILWordService {
// 添加留言
public function append($newLWord) {
// 添加留言前
$this->beforeAppend($newLWord);
// 調用數據訪問層
$dbTask = new LWordDBTask();
$dbTask->append($newLWord);
// 添加留言後
$this->behindAppend($newLWord);
}
// 添加留言前
private function beforeAppend($newLWord) {
// 獲取擴展數組
$extArray = $this->getExtArray();
foreach ($extArray as $ext) {
// 遍歷每一個擴展, 並調用其 beforeAppend 函數
$ext->beforeAppend($newLWord);
}
}
// 添加留言後
private function behindAppend($newLWord) {
// 獲取擴展數組
$extArray = $this->getExtArray();
foreach ($extArray as $ext) {
// 遍歷每一個擴展, 並調用其 behindAppend 函數
$ext->behindAppend($newLWord);
}
}
// 獲取擴展數組,
// 該函數的返回值實際上是 ILWordExtension 接口數組
private function getExtArray() {
return array(
// 檢查權限
new CheckPowerExtension(),
// 檢查內容
new CheckContentExtension(),
// 加分
new AddScoreExtension(),
);
}
};
如果還有新需求,,我們只要再添加ILWordExtension 實現類並且把它注冊到getExtArray函數裡即可。程序從此有了條理,並且算是具備了可擴展性。