《Head First 设计模式》例子的C++实现(10 状态模式)

#《Head First 设计模式》例子的C++实现(10 状态模式)

状态机应用的也很广泛,只不过我们通常都是用一个变量来标志状态,然后用一个大的switch case 结构来处理各个状态。这种写法的缺点就是这个 switch case 会越来越大,越来越难维护。

状态模式则是将各个状态都抽象成了独立的类。每个类只要关心当前状态要处理的事情就可以了,所以简化了代码。缺点也很明显,每个状态建一个类,会导致引入大量的类,增加复杂度。

下面还是贴代码吧,首先是 State 相关的类。所有的 state 类都是用来表示 GumballMachine 的状态的,所以内部需要维护一个指向 GumballMachine 的指针。用这个指针就可以操纵 GumballMachine 做各种事情,从一个状态转换到另一个状态。

class GumballMachine;

class State
{
public:
    State(GumballMachine * gumballMachine)
    {
        m_gumballMachine = gumballMachine;
    }
    virtual void insertQuarter() = 0;
    virtual void ejectQuarter() = 0;
    virtual void turnCrank() = 0;
    virtual void dispense() = 0;
protected:
    GumballMachine * m_gumballMachine;
};

class NoQuarterState: public State
{
public:
    NoQuarterState(GumballMachine * g):State(g){}
    void insertQuarter() override;
    void ejectQuarter() override;
    void turnCrank() override;
    void dispense() override;
};

class SoldOutState:public State
{
public:
    SoldOutState(GumballMachine * g):State(g)
    {
    }

    void insertQuarter() override;
    void ejectQuarter() override;
    void turnCrank() override;
    void dispense() override;
};

class HasQuarterState: public State
{
public:
    HasQuarterState(GumballMachine * g):State(g)
    {
    }

    void insertQuarter() override;
    void ejectQuarter() override;
    void turnCrank() override;
    void dispense() override;
};

class SoldState: public State
{
public:
    SoldState(GumballMachine * g):State(g)
    {
    }

    void insertQuarter() override;
    void ejectQuarter() override;
    void turnCrank() override;
    void dispense() override;
};


class WinnerState: public State
{
public:
    WinnerState(GumballMachine * g):State(g)
    {
    }

    void insertQuarter() override;
    void ejectQuarter() override;
    void turnCrank() override;
    void dispense() override;
};

下面的代码中状态转换调用了 setState() 函数,这里用了个枚举类型。这样可以让 GumballMachine 的接口精简一些。

void NoQuarterState::insertQuarter()
{
    cout << "You inserted a quarter" << endl;
    m_gumballMachine->setState(GumballMachine::HAS_QUARTER);
}
void NoQuarterState::ejectQuarter()
{
    cout << "You haven't inserted a quarter" << endl;
}
void NoQuarterState::turnCrank()
{
    cout << "You turned, but there's no quarter" << endl;
}
void NoQuarterState::dispense()
{
    cout << "You need to pay first" << endl;
}
////////////////////////////////////////////////////////////////////////////
void SoldOutState::insertQuarter()
{
    cout <<"You can't insert a quarter, the machine is sold out" << endl;
}

void SoldOutState:: ejectQuarter()
{
    cout <<"You can't eject, you haven't inserted a quarter yet" << endl;
}

void SoldOutState::turnCrank()
{
    cout <<"You turned, but there are no gumballs" << endl;
}

void SoldOutState::dispense()
{
    cout <<"No gumball dispensed" << endl;
}

//QString SoldOutState::toString()
//{
//    return QString("sold out");
//}


///////////////////////////////////////////////
void SoldState::insertQuarter()
{
    cout << "Please wait, we're already giving you a gumball" << endl;
    m_gumballMachine->setState(GumballMachine::HAS_QUARTER);
}
void SoldState::ejectQuarter()
{
    cout << "Sorry, you already turned the crank" << endl;
}
void SoldState::turnCrank()
{
    cout << "Turning twice doesn't get you another gumball!" << endl;
}
void SoldState::dispense()
{
    m_gumballMachine->releaseBall();
    if(m_gumballMachine->isSoldOut())
    {
        m_gumballMachine->setState(GumballMachine::SOLD_OUT);
    }
    else
    {
        m_gumballMachine->setState(GumballMachine::NO_QUARTER);
    }
}
////////////////////////////////////////////////////////////////////////////
void HasQuarterState::insertQuarter()
{
    cout <<"You can't insert another quarter" << endl;
}

void HasQuarterState::ejectQuarter()
{
    cout <<"Quarter returned" << endl;
    m_gumballMachine->setState(GumballMachine::NO_QUARTER);
}

void HasQuarterState::turnCrank()
{
    cout <<"You turned..." << endl;
    if( rand() % 10 == 0)
    {
        m_gumballMachine->setState(GumballMachine::WINNER);
    }
    else
    {
        m_gumballMachine->setState(GumballMachine::SOLD);
    }
}

void HasQuarterState::dispense()
{
    cout <<"No gumball dispensed" << endl;
}
//////////////////////////////////////////////////////////////////////////////
void WinnerState::insertQuarter()
{
    cout <<"Please wait, we're already giving you a Gumball" << endl;
}

void WinnerState::ejectQuarter()
{
    cout <<"Please wait, we're already giving you a Gumball" << endl;
    //m_gumballMachine->setState(NO_QUARTER);
}

void WinnerState::turnCrank()
{
    cout <<"Turning again doesn't get you another gumball!" << endl;
    m_gumballMachine->releaseBall();

}

void WinnerState::dispense()
{
    cout <<"YOU'RE A WINNER! You get two gumballs for your quarter" << endl;
    m_gumballMachine->releaseBall();
    if(m_gumballMachine->isSoldOut())
    {
        m_gumballMachine->setState(GumballMachine::SOLD_OUT);
    }
    else
    {
        m_gumballMachine->releaseBall();
        if(m_gumballMachine->isSoldOut())
        {
            m_gumballMachine->setState(GumballMachine::SOLD_OUT);
        }
        else
        {
            m_gumballMachine->setState(GumballMachine::NO_QUARTER);
        }
    }
}

下面是 GumballMachine 类:

class GumballMachine
{
public:
    enum STATE{NO_QUARTER, SOLD_OUT, HAS_QUARTER, SOLD, WINNER};
    GumballMachine(int numberGumballs);
    void setState(enum STATE state);
    void releaseBall();
    bool isSoldOut(){return m_count == 0;}
    void refill(int count);
    void insertQuarter(void);
    void ejectQuarter(void);
    void turnCrank(void);
private:
    int m_count;
    State * m_state;
    SoldOutState m_soldOutState;
    NoQuarterState m_noQuarterState;
    HasQuarterState m_hasQuarterState;
    SoldState m_soldState;
    WinnerState m_winnerState;
};

GumballMachine::GumballMachine(int numberGumballs)
    : m_count(numberGumballs),
      m_soldOutState(this),
      m_noQuarterState(this),
      m_hasQuarterState(this),
      m_soldState(this),
      m_winnerState(this)
{
    m_state = &m_noQuarterState;
    if (numberGumballs > 0)
    {
        m_state = &m_noQuarterState;
    }
}


void GumballMachine::setState(enum STATE state)
{
    switch (state)
    {
    case NO_QUARTER:
        m_state = &m_noQuarterState;
        break;
    case SOLD_OUT:
        m_state = &m_soldOutState;
        break;
    case HAS_QUARTER:
        m_state = &m_hasQuarterState;
        break;
    case SOLD:
        m_state = &m_soldState;
        break;
    case WINNER:
        m_state = &m_winnerState;
        break;
    default:
        break;
    }
}
void GumballMachine::releaseBall()
{
    cout << "A gumball comes rolling out the slot..." << endl;
    if (m_count != 0)
    {
        m_count = m_count - 1;
    }
}

void GumballMachine::refill(int count)
{
    m_count = count;
    m_state = &m_noQuarterState;
}

void GumballMachine::insertQuarter(void)
{
    m_state->insertQuarter();
}

void GumballMachine::ejectQuarter(void)
{
    m_state->ejectQuarter();
}

void GumballMachine::turnCrank(void)
{
    m_state->turnCrank();
    m_state->dispense();
}

测试代码如下:

    GumballMachine gumballMachine(5);

    gumballMachine.insertQuarter();
    gumballMachine.turnCrank();

    gumballMachine.insertQuarter();
    gumballMachine.ejectQuarter();
    gumballMachine.turnCrank();

    gumballMachine.insertQuarter();
    gumballMachine.turnCrank();
    gumballMachine.insertQuarter();
    gumballMachine.turnCrank();
    gumballMachine.ejectQuarter();

    gumballMachine.insertQuarter();
    gumballMachine.insertQuarter();
    gumballMachine.turnCrank();
    gumballMachine.insertQuarter();
    gumballMachine.turnCrank();
    gumballMachine.insertQuarter();
    gumballMachine.turnCrank();

输出结果如下:

You inserted a quarter
You turned...
A gumball comes rolling out the slot...
You inserted a quarter
Quarter returned
You turned, but there's no quarter
You need to pay first
You inserted a quarter
You turned...
A gumball comes rolling out the slot...
You inserted a quarter
You turned...
A gumball comes rolling out the slot...
You haven't inserted a quarter
You inserted a quarter
You can't insert another quarter
You turned...
YOU'RE A WINNER! You get two gumballs for your quarter
A gumball comes rolling out the slot...
A gumball comes rolling out the slot...
You can't insert a quarter, the machine is sold out
You turned, but there are no gumballs
No gumball dispensed
You can't insert a quarter, the machine is sold out
You turned, but there are no gumballs
No gumball dispensed

猜你喜欢

转载自blog.csdn.net/liyuanbhu/article/details/83475713
今日推荐