iT邦幫忙

2022 iThome 鐵人賽

DAY 24
0
自我挑戰組

設計模式探索系列 第 24

[Day 24] 狀態模式 (1)

  • 分享至 

  • xImage
  •  

糖果機設計

經過轉接器模式後,先快轉進入到第十章─ 狀態模式
本章的舉例是目前有一台糖果機,想要設計一套軟體讓糖果機變成由軟體控制,要求包含在"投入25美分"後可以選擇"轉動旋鈕"來"售出糖果",或"退出25美分";另外還會檢查糖果機目前的糖果剩餘數量是否可以繼續營業或需要轉為"糖果售罄"狀態。
首先,就從狀態圖轉為程式碼─ 實作狀態機開始。步驟如下:

  1. 定義出所有狀態
    如前面描述的,我們需要以下這些狀態來描述系統的所有情形:
  • 沒有25美分
  • 有25美分
  • 售出糖果
  • 糖果售罄
  1. 定義變數保留目前的狀態
    就是將第一點描述的狀態以變數形式來描述保留,例如以下:
const static int SOLD_OUT = 0;
const static int NO_QUARTER = 1;
const static int HAS_QUARTER = 2;
const static int SOLD = 3;

int state = SOLD_OUT;
  1. 整理系統可能發生的所有動作
    呼叫這些動作也會造成狀態的轉移,包含以下:
  • 投入25美分
  • 退回25美分
  • 轉動旋鈕
  • 投放糖果
  1. 建立狀態機類別
    我們要將第三點的每個動作以方法來實作,並在其中檢查當下狀態來確認相應的行為/ 狀態轉移。例如針對"投入25美分"這個動作,可以以以下方法實作:
void insertQuarter()
{
    if (state == HAS_QUARTER)
    {
        cout << "You can't insert another quarter!" << endl;
    }
    else if (state == NO_QUARTER)
    {
        state = HAS_QUARTER;
        cout << "You inserted a quarter!" << endl;
    }
    else if (state ==SOLD_OUT)
    {
        cout << "You can't insert a quarter! The machine is sold out!" << endl;
    }
    else if (state ==SOLD)
    {
        cout << "Please wait! Gumball coming!" << endl;        
    }
}

糖果機實作

而組織起來,整個糖果機的實作大致如下;我們把轉動旋鈕這個方法也放進去。

class GumballMachine
{
    const static int SOLD_OUT = 0;
    const static int NO_QUARTER = 1;
    const static int HAS_QUARTER = 2;
    const static int SOLD = 3;
    
    int m_state = SOLD_OUT;
    int m_count = 0; // for gumball count
    
    public:
        GumballMachine(int count)
        {
            m_count = count;
            if(count > 0)
            {
                m_state = NO_QUARTER;
            }
        }
        
        void insertQuarter()
        {
            if (m_state == HAS_QUARTER)
            {
                cout << "You can't insert another quarter!" << endl;
            }
            else if (m_state == NO_QUARTER)
            {
                m_state = HAS_QUARTER;
                cout << "You inserted a quarter!" << endl;
            }
            else if (m_state == SOLD_OUT)
            {
                cout << "You can't insert a quarter! The machine is sold out!" << endl;
            }
            else if (m_state == SOLD)
            {
                cout << "Please wait! Gumball coming!" << endl;        
            }
        }
        
        void turnCrank()
        {            
            if (m_state == HAS_QUARTER)
            {
                cout << "You turned..." << endl;
                m_state = SOLD;
                dispense(); // 掉出糖果的方法,一樣在另一個方法中實作
            }
            else if (m_state == NO_QUARTER)
            {
                cout << "You turned but there's no quarter!" << endl;
            }
            else if (m_state == SOLD_OUT)
            {
                cout << "You turned but there's no gumball!" << endl;
            }
            else if (m_state == SOLD)
            {
                cout << "You turned twice!" << endl;        
            }
        }
};

可以試著自行完成ejectQuarter()(退回25美分)與dispense()(掉出糖果)兩個方法,並實際操作看看輸出是否符合預期!

GumballMachine *machine = new GumballMachine(10);
machine->insertQuarter();
machine->turnCrank();
/*-----output-----*/

當改變出現...

此時出現了一個變動... 糖果機廠商希望增加一個特殊條件,當轉動旋鈕時,有10%的機率會掉出2顆糖果!此時狀態機肯定也要進行修改,變成如下的狀態圖,那程式要怎麼做相應的修改呢?
https://ithelp.ithome.com.tw/upload/images/20221010/201400960cMbXz8MBm.png


上一篇
[Day 23] 在VS Code使用C++
下一篇
[Day 25] 狀態模式 (2)
系列文
設計模式探索30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言