狀態模式(State Pattern)

狀態模式:允許物件隨著內部狀態改變而改變行為,就好像物件的類別改變一樣。

有時候成員函式內部,有許多if else的判斷式,後續更新可能持續增加判斷式,以至於最後變得冗長不好維護,這時可以用狀態模式將每個狀態視為一個獨立物件,讓後續需求更動能更好維護,不過也會造成類別變多的情況。

我們用門票販賣機來解釋狀態模式,販賣機有四種狀態,分別是有錢、沒錢、售出門票、門票售完,使用者有投錢和按下買票鍵兩個選擇,販賣機根據目前狀態,會對使用者行為產生對應的結果,我們用TicketMachine類別表示我們的販賣機,建構式參數代表門票數,預設狀態為沒錢,以下為程式碼。

#include <iostream>
using namespace std;

class TicketMachine{
private:
    const static int SOLD_OUT = 0;
    const static int NO_MONEY = 1;
    const static int HAS_MONEY = 2;
    const static int SOLD = 3;
    int m_count;
    int m_state;
public:
    TicketMachine(int count){
        m_count = count;
        m_state = NO_MONEY;
    }
    void insertMoney();
    void buyTicket();
    void dispense();
};
void TicketMachine::insertMoney(){
    if (m_state == HAS_MONEY){ 
        cout << "禁止投錢,已有鈔票" << endl; 
    }
    else if (m_state == NO_MONEY){ 
        m_state = HAS_MONEY; 
        cout << "投入鈔票" << endl; 
    }
    else if (m_state == SOLD_OUT){ 
        cout << "禁止投錢,票已售完" << endl; 
    }
    else if (m_state == SOLD){
        cout << "稍待片刻" << endl;
    }
}
void TicketMachine::buyTicket(){
    if (m_state == HAS_MONEY){
        cout << "買票成功" << endl;
        m_state = SOLD;
        dispense();
    }
    else if (m_state == NO_MONEY){
        cout << "買票失敗,您沒投錢" << endl;
    }
    else if (m_state == SOLD_OUT){
        cout << "買票失敗,票已售完" << endl;
    }
    else if (m_state == SOLD){
        cout << "買票失敗" << endl;
    }
}
void TicketMachine::dispense(){
    cout << "正在售票" << endl;
    m_count--;
    if (m_count == 0){
        m_state = SOLD_OUT;
        cout << "票已售完" << endl;
    }
    else{
        m_state = NO_MONEY;
    }
}
int main(){
    TicketMachine myTicketMachine(2); //預設有2張票
    myTicketMachine.buyTicket();
    myTicketMachine.insertMoney();
    myTicketMachine.buyTicket();
    myTicketMachine.insertMoney();
    myTicketMachine.buyTicket();
    
    return 0;
}

假使我們之後要增加功能,上面的設計除了增加一個狀態,buyTicket()和insertMoney()函式都必須加上更多的判斷式和處理,這邊使用狀態模式,讓每一個狀態實踐各自的動作,以下為程式碼。

#include <iostream>
using namespace std;

class State{
public:
    virtual void insertMoney() = 0;
    virtual void buyTicket() = 0;
};
class Machine{
public:
    State *m_soldOutState;
    State *m_noMoneyState;
    State *m_hasMoneyState;
    State *m_soldState;
    virtual void insertMoney() = 0;
    virtual void buyTicket() = 0;
    virtual void dispense() = 0;
    virtual void setState(State *state) = 0;
};

class NoMoneyState : public State{
private:
    Machine *m_ticketMachine;
public:
    NoMoneyState(Machine *machine){ m_ticketMachine = machine; }
    void insertMoney(){
        m_ticketMachine->setState(m_ticketMachine->m_hasMoneyState);
        cout << "投入鈔票" << endl;
    }
    void buyTicket(){ cout << "買票失敗,您沒投錢" << endl; }
};
class HasMoneyState : public State{
private:
    Machine *m_ticketMachine;
public:
    HasMoneyState(Machine *machine){ m_ticketMachine = machine; }
    void insertMoney(){ cout << "禁止投錢,已有鈔票" << endl; }
    void buyTicket(){
        cout << "買票成功" << endl;
        m_ticketMachine->setState(m_ticketMachine->m_soldState);
        m_ticketMachine->dispense();
    }
};
class SoldState : public State{
private:
    Machine *m_ticketMachine;
public:
    SoldState(Machine *machine){ m_ticketMachine = machine; }
    void insertMoney(){cout << "稍待片刻" << endl;}
    void buyTicket(){cout << "買票失敗" << endl;}
};
class SoldOutState : public State{
private:
    Machine *m_ticketMachine;
public:
    SoldOutState(Machine *machine){ m_ticketMachine = machine; }
    void insertMoney(){ cout << "禁止投錢,票已售完" << endl; }
    void buyTicket(){ cout << "買票失敗,票已售完" << endl; }
};

class TicketMachine : public Machine{
private:
    State * m_state;
    int m_count;
public:
    TicketMachine(int count){
        m_count = count;
        m_soldOutState = new SoldOutState(this);
        m_noMoneyState = new NoMoneyState(this);
        m_hasMoneyState = new HasMoneyState(this);
        m_soldState = new SoldState(this);
        m_state = m_noMoneyState;
    }
    ~TicketMachine(){
        delete m_soldOutState;
        delete m_noMoneyState;
        delete m_hasMoneyState;
        delete m_soldState;
    }
    void insertMoney(){ m_state->insertMoney(); }
    void buyTicket(){ m_state->buyTicket(); }
    void dispense();
    void setState(State *state){ m_state = state; }
};
void TicketMachine::dispense(){
    cout << "正在售票" << endl;
    m_count--;
    if (m_count == 0){
        m_state = m_soldOutState;
        cout << "票已售完" << endl;
    }
    else{
        m_state = m_noMoneyState;
    }
}

int main(){
    TicketMachine myTicketMachine(2); //預設有2張票
    myTicketMachine.buyTicket();
    myTicketMachine.insertMoney();
    myTicketMachine.buyTicket();
    myTicketMachine.insertMoney();
    myTicketMachine.buyTicket();
    
    return 0;
}