這個抽象類別(Class) 就是一個共同的狀態類別, 當作為共同的介面, 定義如下
abstract class State {
abstract insertMoney(money: number): number;
abstract buyButton(): [boolean, number];
abstract cancel(): number;
}
將 enum VendingState 中的每一種狀態建立類別物件, 並繼承上面共同狀態類別, 例如 VendingState.Vending 就建立為
class VendingState extends State {
insertMoney(money: number): number {
}
buyButton(): [boolean, number] {
}
cancel(): number {
}
}
再將先前insertMoney 方法中的 if-else 區塊內的程式碼
if( this._state == VendingState.Vending ) {
this._amount = money;
if( this._amount >= this._price ){
this.vendingLed = true;
this.ledScreen = `已投${this._amount} 元, 請按下購買`;
} else {
this.ledScreen = `已投${this._amount} 元, 請繼續投幣`;
}
this._state = VendingState.InsertingCoins;
return 0;
}
搬到這個 VendingState 狀態類別的 insertMoney 方法內
class VendingState extends State {
insertMoney(money: number): number {
this._amount = money;
if( this._amount >= this._price ){
this.vendingLed = true;
this.ledScreen = `已投${this._amount} 元, 請按下購買`;
} else {
this.ledScreen = `已投${this._amount} 元, 請繼續投幣`;
}
this._state = VendingState.InsertingCoins;
return 0;
}
}
然後把設定狀態的程式碼, 提取為setCurrentState 方法並搬到 State 類別物件內
this._state = VendingState.InsertingCoins;
上面一行程式碼變成
abstract class State {
...
protected void setCurrentState(State newState) {
this._state = newState;
}
}
以此類推, 把先前其他的方法 buyButton(), cancel() 裡面的 if-else 區段程式碼也一併搬到 VendingState 類別物件內
buyButton(): [boolean, number] {
...
if( this._state == VendingState.Vending ) {
return [false, 0];
}
...
}
cancel(): number {
...
if( this._state == VendingState.Vending ) {
return 0;
}
...
}
最後 VendingState 類別物件補齊全了 insertMoney, buyButton, cancel 中的程式碼
class VendingState extends State {
insertMoney(money: number): number {
this._amount = money;
if( this._amount >= this._price ){
this.vendingLed = true;
this.ledScreen = `已投${this._amount} 元, 請按下購買`;
} else {
this.ledScreen = `已投${this._amount} 元, 請繼續投幣`;
}
this.setCurrentState(VendingState.InsertingCoins);
return 0;
}
buyButton(): [boolean, number] {
return [false, 0];
}
cancel(): number {
return 0;
}
}
你可以發現到在 VendingState 販賣中狀態裡,
buyButton() 方法只需回傳不允許購買, 退幣孔不會退任何錢出來.
cancel() 也只需設定退幣孔不會退任何錢出來.
每一個方法中需要實踐的程式碼變更少了, 也不用考慮其他狀態的動作了.
同樣的再繼續實作其他的狀態類別物件
class SoldOutState extends State {
insertMoney(money: number): number {
return money;
}
buyButton(): [boolean, number] {
return [false, 0];
}
cancel(): number {
return 0;
}
}
class InsertingCoinsState extends State {
insertMoney(money: number): number {
if( this._amount >= this._price ){
this.vendingLed = true;
this.ledScreen = `已投${this._amount} 元, 請按下購買`;
return money;
} else {
this.vendingLed = false;
this._amount += money;
this.ledScreen = `已投${this._amount} 元, 請繼續投幣`;
return 0;
}
}
buyButton(): [boolean, number] {
if( this._amount >= this._price ){
let refund = this._amount - this._price;
this._state = VendingState.Vending;
return [true, refund];
} else {
return [false, 0];
}
}
cancel(): number {
if( this._inventory > 0 ) {
this.ledScreen = `庫存${this._inventory} 瓶, 請投幣購買`;
this._state = VendingState.Vending
} else {
this.ledScreen = `飲料已售完`;
this._state = VendingState.SoldOut;
}
let refund = this._amount;
this._amount = 0;
return refund;
}
}
實踐好每一種狀態物件之後, 接下來...