實踐好每一種狀態物件之後, 接下來在VendingMachine 類別物件中, 初始化現在的狀態
class VendingMachine {
private _inventory: number; //販賣機裡面剩餘飲料的庫存數量
private _price: number; //飲料價錢
private _amount: number = 0; //已投幣多少金額
private _state: State;
constructor (inventory: number, price: number) {
this._inventory = inventory;
this._price = price;
this.ledScreen = `庫存${this._inventory} 瓶, 請投幣購買`;
_state = new VendingState(this);
}
insertMoney(money: number): number {
return this._state.insertMoney(money);
}
buyButton(): [boolean, number] {
return this._state.buyButton();
}
cancel(): number {
return this._state.cancel();
}
}
你可以發現原本每一個動作方法insertMoney, buyButton, cancel 都是呼叫 _state 狀態物件的對應的方法. 如此一來, 未來只要加入一種新的狀態, 也只需要實作新的狀態類別物件, 不必修改到 VendingMachine 物件.
狀態模式就是將行為用一個介面封裝起來, 針對不同的狀態去改變其行為.
小明收到一個企劃遊戲需求, 按照要求設計了一個 playFun 方法,
class MyService {
playFun(player: Player): boolean {
let gameInfo = this.getPlayerGameInfo(player);
if( gameInfo == null ) {
this.logError("紀錄ERROR LOG");
return false;
}
if(!this.decreaseLifePoint(player) ){
this.logError("扣體力點數失敗");
return false;
}
if( !this.notifyPlayFunOtherServer(player, "通知其他人玩家加入作戰") ) {
this.addLivePoint(player);
this.logError("紀錄 Game ERROR LOG");
return false;
}
return true;
}
}
這是一個遊戲服務, playFun 方法中, 第一步取得玩家遊戲資料, 如果找不到遊戲資料就紀錄log 錯誤, 如果找到遊戲紀錄就扣除體力值, 然後透過 notifyPlayFunOtherServer 通知其他遊戲伺服器有新玩家加入遊戲作戰.
過了兩天, 小明又收到企劃修改需求, 要求 playFun 再加一個 "加倍送體力" 活動. 所以小明又修改同一個 playFun 的方法, 裡面再加一個....
class MyService {
playFun(player: Player): boolean {
let gameInfo = this.getPlayerGameInfo(player);
if( gameInfo == null ) {
this.logError("紀錄ERROR LOG");
return false;
}
if(!this.decreaseLifePoint(player) ){
this.logError("扣體力點數失敗");
return false;
}
if( !this.notifyPlayFunOtherServer(player, "通知其他人玩家加入作戰") ) {
this.addLivePoint(player);
this.logError("紀錄 Game ERROR LOG");
return false;
}
if( !this.addDoubleLivePoint(player) ) {
this.addLivePoint(player);
this.logError("加倍送體力活動失敗");
return false;
}
return true;
}
}
分析程式碼, 你可以發現這是一條一整道流程, 只要流程當中有一個動作有問題就紀錄LOG 並回傳失敗. 還要把先前扣掉的體力值加回去, 每次辦活動, 都要修改原來的 playFun 方法內容. 這樣加下去 playFun 內容長度越來越長....
在 playFun 方法中
this.getPlayerGameInfo(player)
this.decreaseLifePoint(player)
this.notifyPlayFunOtherServer(player, ...)
this.addDoubleLivePoint(player)
// 增加 Code
我們的目標是讓 playFun 容易擴充, 在不修改現有 playFun 程式碼的情形就能增加新的行為, 或是減少行為, 這樣的設計具有彈性, 可以接受行銷企劃們經常修改需求的變動. 這看起來根本不可能?
首先我們要設計一個裝飾者共同介面
abstract class MyService {
protected _service: MyService;
constructor(service: MyService) {
this._service = service;
}
abstract playFun(player: Player): boolean;
}
在 playFun 方法中
this.getPlayerGameInfo(player)
this.decreaseLifePoint(player)
this.notifyPlayFunOtherServer(player, ...)
this.addDoubleLivePoint(player)
最主要是核心動作是
this.getPlayerGameInfo(player)
this.decreaseLifePoint(player)
然後建立 PlayFunService 類別物件, 這物件只需要專心進行 playFun 真正的那兩個動作就好
class PlayFunService extends MyService {
constructor() {
super(null);
}
playFun(player: Player): boolean {
let gameInfo = this.getPlayerGameInfo(player);
if( gameInfo == null ) {
return false;
}
if(!this.decreaseLifePoint(player) ){
return false;
}
return true;
}
...
}
然後我們設計例外處理錯誤類別物件, 在設計例外處理類別之前, 我先介紹一下Javascript 的錯誤處理介紹