#《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