相信大家在外面都有用過飲料販賣機, 假設有一台小王牌飲料販賣機, 販賣機上只有一個販賣按鈕, 一個顯示液晶螢幕, 一個投幣孔, 一個退幣洞和一個出貨洞. 目前販賣機只賣一種飲料20元, 庫存2瓶.
假設小張投了10元. 此時販賣按鈕燈不會亮起, 投幣孔持續開放, 螢幕上顯示 "已投10 元, 請繼續投幣".
小張再投5元, 此時販賣按鈕燈不會亮起, 投幣孔持續開放, 螢幕上顯示 "已投15 元, 請繼續投幣".
小張再投10元, 此時販賣按鈕燈亮起, 投幣孔關閉, 螢幕上顯示 "已投25 元, 請按下購買".
當小張按下販賣按鈕, 販賣機退5元, 販賣機此時剩下1瓶, 投幣孔開啟, 販賣按鈕燈不會亮, 螢幕上顯示 "庫存1 瓶, 請投幣購買".
你要設計販賣機物件(VendingMachine), 販賣機物件提供
以下屬性
以下功能
小明拿到這個需求, 馬上設計了以下物件
class VendingMachine {
private _inventory: number; //販賣機裡面剩餘飲料的庫存數量
private _price: number; //飲料價錢
private _amount: number = 0; //已投幣多少金額
private _canInsertMoney: boolean = true; //是否能夠投幣
private _canBuy: boolean = false;
constructor (inventory: number, price: number) {
this._inventory = inventory;
this._price = price;
this.ledScreen = `庫存${this._inventory} 瓶, 請投幣購買`;
}
ledScreen: string = "";
vendingLed: boolean = false;
insertMoney(money: number): number {
if( !this._canInsertMoney ) {
return money;
}
this._amount += money;
if( this._amount >= this._price ){
this.vendingLed = true;
this.ledScreen = `已投${this._amount} 元, 請按下購買`;
this._canBuy = true;
return 0;
} else {
this.vendingLed = false;
this.ledScreen = `已投${this._amount} 元, 請繼續投幣`;
this._canBuy = false;
return 0;
}
}
buyButton(): [boolean, number] {
if( !this._canBuy ) {
return [false, 0];
}
let refund = this._amount - this._price;
this.cancel();
return [true, refund];
}
cancel(): number {
if( this._inventory > 0 ) {
this.ledScreen = `庫存${this._inventory} 瓶, 請投幣購買`;
this._canInsertMoney = true;
} else {
this.ledScreen = `飲料已售完`;
this._canInsertMoney = false;
}
let refund = this._amount;
this._amount = 0;
return refund;
}
}
觀察上面的程式碼, 你會發現這個物件有許多變數狀態, 每一個功能(function) 隨著變數狀態改變而改變function 的行為. 甚至某些公開方法(function) 故意呼叫其它的公開方法(function) 來改變物件內部的狀態.
有時候某些公開方法(function) 內部有許多if else 判斷式, 假設後續要增加新的功能(function), 就可能會持續增加變數狀態用來判斷新功能是否開啟或關閉.
最後程式碼會變得冗長不好維護, 還要擔心每一個功能是否會互相干擾, 例如其他功能的開啟或關閉. 這個時後我們可以用狀態模式將每個狀態視為一個獨立物件, 讓後續需求更動時, 程式碼比較能更好維護.
分析販賣機的行為, 找出所有狀態, 我們可以把販賣機狀態分類為