iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 22
1

還記得剛剛原本的 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);
   }
}
  • 最外層的物件 WriteLogErrorDecorator 呼叫裡面的 PlayFunErrorHandleDecorator.playFun() 如果有任何例外錯誤就寫LOG
  • 第二層物件 PlayFunErrorHandleDecorator 呼叫裡面的 AddDoubleLivePointAdvertisingDecorator.playFun() 如果有 PlayFunError 例外錯誤, 就把體力值恢復
  • 第三層物件 AddDoubleLivePointAdvertisingDecorator 呼叫裡面的 playFun() 並舉辦加倍送體力值活動
  • 第四層物件 NotifyOtherServerDecorator 也呼叫裡面的 playFun() 並執行通知其他遊戲伺服器動作
  • 最裡面的物件 PlayFunService 只有執行跟 playFun 核心的動作

假設小明又收到企畫的情人節活動動作要加到 playFun 裡面去, 小明只需要新增 "情人節活動" 物件繼承 MyService, 實作 playFun 方法. 裡面只需呼叫內層的 playFun 並執行情人節活動有關的程式碼.

再把 "情人節活動" 物件 "裝飾" 進去 service.

至於上面長長的 new..new..new.. 初始化物件程式碼, 我們可以另外再想辦法封裝起來.


上一篇
錯誤種類 - 21
下一篇
Typescript 進階應用 - 23
系列文
為什麼世界需要Typescript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言