目录
一.专栏简介
本专栏是我学习《head first》设计模式的笔记。这本书中是用Java语言为基础的,我将用C++语言重写一遍,并且详细讲述其中的设计模式,涉及是什么,为什么,怎么做,自己的心得等等。希望阅读者在读完我的这个专题后,也能在开发中灵活且正确的使用,或者在面对面试官时,能够自信地说自己熟悉常用设计模式。
本章将开始状态模式的学习,这是策略模式的双胞胎兄弟。
二.状态图

这是一个状态图,每个圆圈是一个状态,总共五个状态,箭头上的文字是行为,至少五种行为。
我们打算把状态对象封装进它们的类,让后在动作发生时让机器类委托给当前状态。在这里,我们遵循以下设计原则,因此最后应该得到一个更容易维护的设计。我们打算这么做:
- 首先,我们打算定义一个State接口,里面针对糖果机内的每个动作有一个方法。
- 然后我们打算为机器的每个状态实现一个State类。这些类将负责在相应状态下的机器行为。
- 最后,我们打算去除所有条件代码,把工作委托给State类。
我们不仅遵循设计原则,正如我们将看到的,实际上我们实现了状态模式。不过,在重新编写我们代码后,我们再学习官方的状态模式......
三.State接口和类
首先我们来为State创建一个接口,所有状态都要实现它:

把设计中的每个状态封装进一个实现State接口的类。

是时候实现状态了,我们知道了我们要什么行为,只需要把它变成代码。代码如下:
state.h:
cpp
#pragma once
#include <iostream>
using namespace std;
struct State
{
virtual void insertQuarter() = 0;
virtual void ejectQuarter() = 0;
virtual void turnCrank() = 0;
virtual void dispense() = 0;
};
class NoQuarterState : public State
{
public:
NoQuarterState(GumballMachine* gumballMachine):
_gumballMachine(gumballMachine)
{}
void insertQuarter() override
{
cout << "你放入了25美分" << endl;
_gumballMachine->setState(_gumballMachine->getHasQuarterState());
}
void ejectQuarter() override
{
cerr << "你尚未投入25分钱" << endl;
}
void turnCrank() override
{
cerr << "你转动曲柄但没有25分钱" << endl;
}
void dispense() override
{
cerr << "你需要先投钱" << endl;
}
private:
GumballMachine* _gumballMachine;
};
class HasQuarterState : public State
{
public:
HasQuarterState(GumballMachine* gumballMachine):
_gumballMachine(gumballMachine)
{}
void insertQuarter() override
{
cerr << "你不能投入另一个25分钱" << endl;
}
void ejectQuarter() override
{
cout << "已退回25分钱" << endl;
_gumballMachine->setState(_gumballMachine->getNoQuarterState());
}
void turnCrank() override
{
cout << "已转动" << endl;
_gumballMachine->setState(_gumballMachine->getSoldState());
}
void dispense() override
{
cerr << "没发放糖果" << endl;
}
private:
GumballMachine* _gumballMachine;
};
class SoldState : public State
{
public:
SoldState(GumballMachine* gumballMachine):
_gumballMachine(gumballMachine)
{}
void insertQuarter() override
{
cerr << "请稍后,我们马上给你糖果" << endl;
}
void ejectQuarter() override
{
cerr << "抱歉,你已经转过曲柄" << endl;
_gumballMachine->setState(_gumballMachine->getNoQuarterState());
}
void turnCrank() override
{
cerr << "不会因为转两次就再给你一颗糖果" << endl;
}
void dispense() override
{
cerr << "发放糖果" << endl;
_gumballMachine->releaseBall();
int count = _gumballMachine->getCount();
if(count) _gumballMachine->setState(_gumballMachine->getNoQuarterState());
else
{
cerr << "没糖果了" << endl;
_gumballMachine->setState(_gumballMachine->getSoldOutState());
}
}
private:
GumballMachine* _gumballMachine;
};
class SoldOutState : public State
{
public:
SoldOutState(GumballMachine* gumballMachine) :
_gumballMachine(gumballMachine)
{}
void insertQuarter() override
{
cerr << "机器已经售罄" << endl;
}
void ejectQuarter() override
{
cerr << "你还没有投入25分钱" << endl;
}
void turnCrank() override
{
cerr << "机器已经售罄" << endl;
}
void dispense() override
{
cerr << "机器已经售罄" << endl;
}
private:
GumballMachine* _gumballMachine;
};
class WinnerState : public State
{
public:
WinnerState(GumballMachine* gumballMachine):
_gumballMachine(gumballMachine)
{}
void insertQuarter() override
{
cerr << "请稍后,我们马上给你糖果" << endl;
}
void ejectQuarter() override
{
cerr << "抱歉,你已经转过曲柄" << endl;
}
void turnCrank() override
{
cerr << "不会因为转两次就再给你一颗糖果" << endl;
}
void dispense() override
{
_gumballMachine->releaseBall();
int count1 = _gumballMachine->getCount();
if (count1 > 0)
{
_gumballMachine->releaseBall();
int count2 = _gumballMachine->getCount();
if (count2) _gumballMachine->setState(_gumballMachine->getNoQuarterState());
else _gumballMachine->setState(_gumballMachine->getSoldOutState());
}
}
private:
GumballMachine* _gumballMachine;
};
class GumballMachine
{
public:
GumballMachine(int numberGumballs) :
soldOutState(new SoldOutState(this)),
noQuarterState(new NoQuarterState(this)),
hasQuarterState(new HasQuarterState(this)),
soldState(new SoldState(this)),
count(numberGumballs)
{
if (count) state = noQuarterState;
else state = soldOutState;
}
void insertQuarter() { state->insertQuarter(); }
void ejectQuarter() { state->ejectQuarter(); }
void turnCrank()
{
state->turnCrank();
state->dispense();
}
void setState(State* s) { state = s; }
void releaseBall()
{
cout << "放出一颗糖果" << endl;
if (count > 0) --count;
}
int getCount() { return count; }
State* getSoldOutState() { return soldOutState; }
State* getNoQuarterState() { return noQuarterState; }
State* getHasQuarterState() { return hasQuarterState; }
State* getSoldState() { return soldState; }
private:
State* soldOutState;
State* noQuarterState;
State* hasQuarterState;
State* soldState;
int count = 0;
State* state;
};
- 把每个状态的行为局部化到它自己的类中。
- 避免所有烦人,难以维护的if语句。
- 通过添加新的状态类(我们马上就这样做),使得每一个状态对修改关闭,糖果机对扩展开放。
- 创建更紧密映射万能糖果公司状态图、更容易阅读和理解的代码以及类结构。
现在继续来看看我们已经做了的功能方面:

文字描述如图中所示:

四.状态模式和定义
状态模式允许对象在内部状态改变时改变其行为。对象看起来好像改变了它的类。
实际上我们知道我们是在使用组合来简单引用不同的状态对象,造成类改变的假象。
状态模式类图:

五.策略模式和状态模式的区别
对于状态模式,我们把一组行为封装进状态对象,context随时(把责任)委托给这些状态之一。随着时间的推移,当前状态不断在各个状态对象之间改变,以反映context的内部状态,因此,context的行为也随着时间的推移而改变。客户通常对状态对象知道得很少,甚至不知道。
对于策略,客户通常指定context要组合的策略对象。现在,虽然模式提供了弹性,可以在运行时改变策略对象,但对于一个context对象来说,经常有一个最适合的策略对象。
一般来说,把策略模式想成子类化的一个弹性替代方案;如果你使用继承定义了一个类的行为,那么你就和这个行为黏在一起了,很难改变。有了策略,我们可以通过组合不同的对象改变行为。
把状态模式想成在context中放进许多条件的一个替代方案,通过把行为封装进状态对象,你可以简单地在context中改变状态对象来改变其行为。
六.完整代码和运行结果
完整代码中我们加入了赢家状态。
state.h:
cpp
#pragma once
#include <iostream>
#include <ctime>
using namespace std;
struct State
{
virtual void insertQuarter() = 0;
virtual void ejectQuarter() = 0;
virtual void turnCrank() = 0;
virtual void dispense() = 0;
virtual void refill() = 0;
};
class GumballMachine
{
public:
GumballMachine(int numberGumballs);
void insertQuarter() { state->insertQuarter(); }
void ejectQuarter() { state->ejectQuarter(); }
void turnCrank()
{
state->turnCrank();
state->dispense();
}
void setState(State* s) { state = s; }
void releaseBall()
{
cout << "放出一颗糖果" << endl;
if (count > 0) --count;
}
void refill(int addCount)
{
count += addCount;
cout << "加入了" << addCount << "颗糖果,现在糖果总数为" << count << "颗" << endl;
state->refill();
}
int getCount() const { return count; }
State* getSoldOutState() { return soldOutState; }
State* getNoQuarterState() { return noQuarterState; }
State* getHasQuarterState() { return hasQuarterState; }
State* getSoldState() { return soldState; }
State* getWinnerState() { return winnerState; }
private:
State* soldOutState;
State* noQuarterState;
State* hasQuarterState;
State* soldState;
State* winnerState;
int count = 0;
State* state;
};
class NoQuarterState : public State
{
public:
NoQuarterState(GumballMachine* gumballMachine):
_gumballMachine(gumballMachine)
{}
void insertQuarter() override
{
cout << "你放入了25美分" << endl;
_gumballMachine->setState(_gumballMachine->getHasQuarterState());
}
void ejectQuarter() override
{
cerr << "你尚未投入25分钱" << endl;
}
void turnCrank() override
{
cerr << "你转动曲柄但没有25分钱" << endl;
}
void dispense() override
{
cerr << "你需要先投钱" << endl;
}
void refill() override
{
}
private:
GumballMachine* _gumballMachine;
};
class HasQuarterState : public State
{
public:
HasQuarterState(GumballMachine* gumballMachine):
_gumballMachine(gumballMachine)
{
srand((unsigned int)time(NULL));
}
void insertQuarter() override
{
cerr << "你不能投入另一个25分钱" << endl;
}
void ejectQuarter() override
{
cout << "已退回25分钱" << endl;
_gumballMachine->setState(_gumballMachine->getNoQuarterState());
}
void turnCrank() override
{
cout << "已转动" << endl;
int r = rand() % 10;
if(r == 0 && _gumballMachine->getCount() > 1) _gumballMachine->setState(_gumballMachine->getWinnerState());
else _gumballMachine->setState(_gumballMachine->getSoldState());
}
void dispense() override
{
cerr << "没发放糖果" << endl;
}
void refill() override
{
}
private:
GumballMachine* _gumballMachine;
};
class SoldState : public State
{
public:
SoldState(GumballMachine* gumballMachine):
_gumballMachine(gumballMachine)
{}
void insertQuarter() override
{
cerr << "请稍后,我们马上给你糖果" << endl;
}
void ejectQuarter() override
{
cerr << "抱歉,你已经转过曲柄" << endl;
_gumballMachine->setState(_gumballMachine->getNoQuarterState());
}
void turnCrank() override
{
cerr << "不会因为转两次就再给你一颗糖果" << endl;
}
void dispense() override
{
cout << "发放糖果" << endl;
_gumballMachine->releaseBall();
int count = _gumballMachine->getCount();
if(count) _gumballMachine->setState(_gumballMachine->getNoQuarterState());
else
{
cout << "没糖果了" << endl;
_gumballMachine->setState(_gumballMachine->getSoldOutState());
}
}
void refill() override
{
}
private:
GumballMachine* _gumballMachine;
};
class SoldOutState : public State
{
public:
SoldOutState(GumballMachine* gumballMachine) :
_gumballMachine(gumballMachine)
{}
void insertQuarter() override
{
cerr << "机器已经售罄" << endl;
}
void ejectQuarter() override
{
cerr << "你还没有投入25分钱" << endl;
}
void turnCrank() override
{
cerr << "机器已经售罄" << endl;
}
void dispense() override
{
cerr << "机器已经售罄" << endl;
}
void refill() override
{
_gumballMachine->setState(_gumballMachine->getNoQuarterState());
}
private:
GumballMachine* _gumballMachine;
};
class WinnerState : public State
{
public:
WinnerState(GumballMachine* gumballMachine):
_gumballMachine(gumballMachine)
{}
void insertQuarter() override
{
cerr << "请稍后,我们马上给你糖果" << endl;
}
void ejectQuarter() override
{
cerr << "抱歉,你已经转过曲柄" << endl;
}
void turnCrank() override
{
cerr << "不会因为转两次就再给你一颗糖果" << endl;
}
void dispense() override
{
_gumballMachine->releaseBall();
int count1 = _gumballMachine->getCount();
if (count1 > 0)
{
_gumballMachine->releaseBall();
cout << "你是赢家,你获得了两颗糖果" << endl;
int count2 = _gumballMachine->getCount();
if (count2) _gumballMachine->setState(_gumballMachine->getNoQuarterState());
else _gumballMachine->setState(_gumballMachine->getSoldOutState());
}
else
{
cout << "没糖果了" << endl;
_gumballMachine->setState(_gumballMachine->getSoldOutState());
}
}
void refill() override
{
}
private:
GumballMachine* _gumballMachine;
};
GumballMachine::GumballMachine(int numberGumballs) :
soldOutState(new SoldOutState(this)),
noQuarterState(new NoQuarterState(this)),
hasQuarterState(new HasQuarterState(this)),
soldState(new SoldState(this)),
winnerState(new WinnerState(this)),
count(numberGumballs)
{
if (count) state = noQuarterState;
else state = soldOutState;
}
在代码中我们加入了赢家状态,并且在有25分钱状态里我们在转动把柄后用随机数判断是不是赢家,进而跳转到不同的状态。另外还新增了refill方法,支持往机器中加入更多糖果。
main.cpp:
cpp
#include "state.h"
int main()
{
GumballMachine gumballMachine(5);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.refill(10);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
return 0;
}
运行结果:

七.总结
- 状态模式允许一个对象基于内部状态拥有许多不同的行为。
- 和过程式状态机不同,状态模式用真正的类代表每个状态。
- Context把行为委托给所组合的当前状态对象。
- 通过把每个状态封装进一个类,我们把以后需要做的任何改变局部化了。
- 状态和策略模式有相同的类图,但它们的意图不同。
- 策略模式通常会用行为或算法来配置Context类。
- 状态模式允许Context随着状态的改变来改变行为。
- 状态迁移可以由State类或Context类来控制。
- 使用状态模式通常会导致设计中类数目增加。
- 状态类可以在多个Context实例之间共享。