還記得剛剛原本的 playFun 方法中流程剩下下面兩個動作
this.notifyPlayFunOtherServer(player, ...)
this.addDoubleLivePoint(player)
我們可以將為這兩個動作一一建立類別物件, 首先建立 NotifyOtherServerService 類別物件, 並把對應的程式碼搬過來...
class NotifyOtherServerDecorator extends MyService {
constructor(service: MyService) {
super(service);
}
playFun(player: Player): boolean {
service.playFun(player);
if( !this.notifyPlayFunOtherServer(player, "通知其他人玩家加入作戰") ) {
this.addLivePoint(player);
this.logError("紀錄 Game ERROR LOG");
return false;
}
}
}
然後我們得移除 logError 和 addLivePoint 方法, 讓每一個動作乾淨一點...
class NotifyOtherServerDecorator extends MyService {
constructor(service: MyService) {
super(service);
}
playFun(player: Player): boolean {
service.playFun(player);
return this.notifyPlayFunOtherServer(player, "通知其他人玩家加入作戰");
}
}
然後在 notifyPlayFunOtherServer 方法裡面寫 "如果發生錯誤, 我們就丟 PlayFunError 例外錯誤"
notifyPlayFunOtherServer(player: PlayerInfo): boolean {
if( any error ) { //虛擬碼
throw new PlayFunError("play fun error");
}
return true;
}
另一個 addDoubleLivePoint 加倍體力活動也建立物件
class AddDoubleLivePointAdvertinsingDecorator extends MyService {
constructor(service: MyService) {
super(service);
}
playFun(player: Player): boolean {
if( !this.addDoubleLivePoint(player) ) {
this.addLivePoint(player);
this.logError("加倍送體力活動失敗");
return false;
}
}
}
同樣地我們也拿掉 logError, addLivePoint 跟加倍體力活動無關的程式碼
class AddDoubleLivePointAdvertisingDecorator extends MyService {
constructor(service: MyService) {
super(service);
}
playFun(player: Player): boolean {
service.playFun(player);
return this.addDoubleLivePoint(player);
}
}
以此類推, 任何其他活動只要有錯誤我們都一律丟出 PlayFunError 例外錯誤, 然後我們再建立 "寫LogError" 處理物件, 這個物件只專注於應用程式有沒有發生例外錯誤, 如果有例外錯誤, 這個物件就幫忙寫log.
class WriteLogErrorDecorator extends MyService {
constructor(service: MyService) {
super(service);
}
playFun(player: Player): boolean {
try {
return service.playFun(player);
}catch(e) {
this.writeLogError(e);
return false;
}
}
}
接著設計類別物件, 專門在應用程式發生錯誤的時候, 把扣除的體力值加回來
class PlayFunErrorHandleDecorator implements IMyService {
private _service: IMyService;
constructor(service: IMyService) {
this._service = service;
}
playFun(player: Player): boolean {
try {
return service.playFun(player);
}catch(e) {
if(e instanceof PlayFunError) {
this.addLivePoint(player);
}
throw e;
}
}
}
最後將這些物件像裝飾蛋糕一樣一層一層的裝上去
let server = new WriteLogErrorDecorator(new PlayFunErrorHandleDecorator(new AddDoubleLivePointAdvertisingDecorator(new NotifyOtherServerDecorator(new PlayFunService()))));
程式碼寫成長長的一行很難看, 我們先排版一下
let service = new WriteLogErrorDecorator(
new PlayFunErrorHandleDecorator(
new AddDoubleLivePointAdvertisingDecorator(
new NotifyOtherServerDecorator(
new PlayFunService()))));
最後在呼叫端呼叫這個物件
class MyService {
playFun(player: Player): boolean {
let service = new WriteLogErrorDecorator(
new PlayFunErrorHandleDecorator(
new AddDoubleLivePointAdvertisingDecorator(
new NotifyOtherServerDecorator(
new PlayFunService()))));
return service.playFun(player);
}
}
假設小明又收到企畫的情人節活動動作要加到 playFun 裡面去, 小明只需要新增 "情人節活動" 物件繼承 MyService, 實作 playFun 方法. 裡面只需呼叫內層的 playFun 並執行情人節活動有關的程式碼.
再把 "情人節活動" 物件 "裝飾" 進去 service.
至於上面長長的 new..new..new.. 初始化物件程式碼, 我們可以另外再想辦法封裝起來.