1.场景分析
假如我们需要为一家糖果公司制作一个糖果机。糖果机的运行机制如下面的状态图所示:
从图中我们知道,状态机有四个状态:没有25分钱、有25分钱、售出糖果、糖果售罄。所以我们可以通过一个枚举类型来定义每个状态:
enum class MachineState
{
SOLD_OUT,
NO_QUARTER,
HAS_QUARTER,
SOLD
};
其有四个操作:投入25分、退回25分、转动曲柄、发放糖果,可以分别用四个函数来表示这四种动作:
void insertQuarter();
void ejectQuarter();
void turnCrank();
void dispense();
最后我们写出来的糖果机如下所示:
class GumballMachine
{
public:
enum class MachineState
{
SOLD_OUT,
NO_QUARTER,
HAS_QUARTER,
SOLD
};
void insertQuarter()
{
switch (_state)
{
case MachineState::SOLD_OUT:
cout << "you can not insert a quarter, the machine is sold out" << endl;
break;
case MachineState::NO_QUARTER:
cout << "you insert a quarter" << endl;
_state = MachineState::HAS_QUARTER;
break;
case MachineState::HAS_QUARTER:
cout << "you can not insert another quarter" << endl;
break;
case MachineState::SOLD:
cout << "please wait, we are already giving you a gumball" << endl;
break;
default:
break;
}
}
void ejectQuarter()
{
switch (_state)
{
case MachineState::SOLD_OUT:
cout << "you can not eject, you haven't inserted a quarter" << endl;
break;
case MachineState::NO_QUARTER:
cout << "you haven't inserted a quarter" << endl;
break;
case MachineState::HAS_QUARTER:
cout << "Quarter returned" << endl;
_state = MachineState::HAS_QUARTER;
break;
case MachineState::SOLD:
cout << "sorry, you already turned the crank" << endl;
break;
default:
break;
}
}
void turnCrank()
{
switch (_state)
{
case MachineState::SOLD_OUT:
cout << "you turned, but there are no gumballs" << endl;
break;
case MachineState::NO_QUARTER:
cout << "you turned, but there is no quarter" << endl;
break;
case MachineState::HAS_QUARTER:
cout << "you turned" << endl;
_state = MachineState::SOLD;
dispense();
break;
case MachineState::SOLD:
cout << "turning twice doesn't get you another gumball" << endl;
break;
default:
break;
}
}
void dispense()
{
switch (_state)
{
case MachineState::SOLD_OUT:
cout << "no gumball dispensed" << endl;
break;
case MachineState::NO_QUARTER:
cout << "you need to pay first" << endl;
break;
case MachineState::HAS_QUARTER:
cout << "no gumball dispensed" << endl;
break;
case MachineState::SOLD:
cout << "A gumball come rolling out the slot" << endl;
--_gumballCount;
if (_gumballCount == 0)
_state = MachineState::SOLD_OUT;
else
_state = MachineState::NO_QUARTER;
break;
default:
break;
}
}
private:
MachineState _state = MachineState::NO_QUARTER;
int _gumballCount = 0;
};
从上述代码中,我们可以看到大量的switch语句,状态管理十分混乱,而且如果新添加一个状态将十分艰难,代码变得难以扩展。这时我们可以通过状态模式来改善我们的代码设计。
首先我们定义一个State基类,在这个类中,糖果机的每个动作都有一个对应的方法。
class MachineState
{
public:
virtual void insertQuarter(){}
virtual void ejectQuarter(){}
virtual void turnCrank(){}
virtual void dispense(){}
};
然后我们为机器的每个状态实现状态类。这些类负责在对应状态下进行机器的行为。
最后,我们要摆脱旧的条件代码,取而代之的方法是将动作委托到状态类。
最后我们重构后的糖果机如下图:
#include <iostream>
using namespace std;
class MachineState
{
public:
virtual void insertQuarter(){}
virtual void ejectQuarter(){}
virtual void turnCrank(){}
virtual void dispense(){}
};
class GumballMachine
{
public:
void setState(MachineState *state)
{
if (_state)
delete _state;
_state = state;
}
int getGumballCount() { return _gumballCount; }
void releaseGumball() { --_gumballCount; }
void insertQuarter(){ if (!_state) return; _state->insertQuarter(); }
void ejectQuarter(){ if (!_state) return; _state->ejectQuarter(); }
void turnCrank(){ if (!_state) return; _state->turnCrank(); }
void dispense(){ if (!_state) return; _state->dispense(); }
private:
MachineState *_state = nullptr;
int _gumballCount = 0;
};
class NoQuarterState : public MachineState
{
public:
NoQuarterState(GumballMachine *machine) :_machine(machine){}
void insertQuarter() override
{
_machine->setState(new HasQuarterState(_machine));
}
private:
GumballMachine * _machine = nullptr;
};
class HasQuarterState : public MachineState
{
public:
HasQuarterState(GumballMachine *machine) :_machine(machine){}
void ejectQuarter() override
{
_machine->setState(new SoldState(_machine));
}
private:
GumballMachine * _machine = nullptr;
};
class SoldState : public MachineState
{
public:
SoldState(GumballMachine *machine) :_machine(machine){}
void dispense() override
{
_machine->releaseGumball();
if (_machine->getGumballCount() > 0)
_machine->setState(new NoQuarterState(_machine));
else
_machine->setState(new SoldOutState(_machine));
}
private:
GumballMachine * _machine = nullptr;
};
class SoldOutState : public MachineState
{
public:
SoldOutState(GumballMachine *machine) :_machine(machine){}
private:
GumballMachine * _machine = nullptr;
};
2.意图
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
3.适用性
- 一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
- 一个操作中含有庞大的多分支条件语句,且这些分支依赖于对象的状态,这些状态通常用一个或多个枚举常量表示。
4.结构
Context:环境,定义客户端感兴趣的接口。
State:环境所处的状态,定义Context与特定状态相关的行为。
5.效果
1>它将与特定状态相关的行为局部化,并且将不同状态的行为分割开。State模式将所有与一个特定状态相关的行为都放入一个对象中。因为所有与状态相关的代码都存在于某一个State子类中,所以通过定义新的子类可以很容易的增加新的状态和装换。这样我们就避免了使用一大堆相似的条件语句来管理状态,但是同时也引入了另一个问题,就是增加了子类的数量,相对于当个类的实现来说不够紧凑。我们让每一个状态“对修改关闭”,而让context“对扩展开放”。
2>它使得状态转换显示化。当一个对象仅以内部数据值来定义当前状态时,其状态仅表现在对一些变量的赋值,这不够明确。为不同的状态引入独立的对象使得装换变得更加明确。
3>State对象可以被共享,如果State对象没有实例变量——即它们表示的状态完全以它们的类型来编码——那么各Context可以共享一个状态对象。当状态以这种方式被共享时,它们必定是没有内部状态而只有行为的轻量级对象(Flyweight)。
6.实现
1>谁定义状态转换。有两种选择,第一种是在Context中实现所有状态转换的操作,另一种是由State子类自身来指定它们的后续状态以及何时进行转换。通常第二种方法更加灵活,但是这样做则一个State子类至少拥有另一个State子类的相关信息,增加了子类之间的耦合性。我们前面例子中采用的第二种实现方法。
2>基于表的另一种方法。
3>创建和销毁State对象。有两种选择:(1)仅当需要State对象时才创建它们并在使用后销毁。(2)提前创建它们并且始终不销毁它们。当上下文不经常改变状态时,第一种比较适合,如果状态改变很频繁,则第二种比较合适。
7.代码示例
我们来实现一个表示网络连接的类TCPConnection,并用状态模式来实现其处于不同的网络状态。
#include <iostream>
using namespace std;
class TCPConnection;
class TCPState
{
public:
virtual void activeOpen(TCPConnection *tcpConnection){}
virtual void passiveOpen(TCPConnection *tcpConnection){}
virtual void close(TCPConnection *tcpConnection){}
virtual void send(TCPConnection *tcpConnection){}
virtual void acknowledge(TCPConnection *tcpConnection){}
virtual void synchronize(TCPConnection *tcpConnection){}
protected:
void changeState(TCPConnection *tcpConnection, TCPState *tcpState);
};
class TCPCloseState : public TCPState
{
public:
static TCPCloseState *create()
{
return new TCPCloseState();
}
void activeOpen(TCPConnection *tcpConnection);
};
class TCPOpenState : public TCPState
{
public:
static TCPOpenState *create()
{
return new TCPOpenState();
}
//省略其他状态转化实现
...
};
class TCPConnection
{
public:
TCPConnection()
{
_state = TCPCloseState::create();
}
void activeOpen() { if (!_state) return; _state->activeOpen(this); }
void passiveOpen() { if (!_state) return; _state->passiveOpen(this); }
void close() { if (!_state) return; _state->close(this); }
void send() { if (!_state) return; _state->send(this); }
void acknowledge() { if (!_state) return; _state->acknowledge(this); }
void synchronize() { if (!_state) return; _state->synchronize(this); }
private:
friend TCPState;
void changeState(TCPState *state);
TCPState *_state = nullptr;
};
void TCPState::changeState(TCPConnection *tcpConnection, TCPState *tcpState)
{
tcpConnection->changeState(tcpState);
}
void TCPCloseState::activeOpen(TCPConnection *tcpConnection)
{
changeState(tcpConnection, TCPOpenState::create());
}