动机
在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生变化。
比如
- 文档处于只读状态,其支持的行为和读写状态支持的行为就可能完全不同
如何在运行时根据对象的状态来透明地更改对象的行为?而不会为对象操作和状态转换之间引入紧耦合?
代码:网络的应用
原始代码
状态不同,NetworkProcessor
的行为也不同
//网络状态
enum NetworkState
{
Network_Open,
Network_Close,
Network_Connect,
};
//网络处理者
class NetworkProcessor{
NetworkState state;
public:
void Operation1(){
if (state == Network_Open){
//打开时,有这些行为
//**********
state = Network_Close; //打开之后,状态又发生变化
}
else if (state == Network_Close){
//关闭时,有这些行为
//..........
state = Network_Connect;
}
else if (state == Network_Connect){
//连接时,有这些行为
//$$$$$$$$$$
state = Network_Open;
}
}
public void Operation2(){
if (state == Network_Open){
//**********
state = Network_Connect;
}
else if (state == Network_Close){
//.....
state = Network_Open;
}
else if (state == Network_Connect){
//$$$$$$$$$$
state = Network_Close;
}
}
public void Operation3(){
}
};
引入状态模式
未来会不会有其他状态,比如Network_Wait
。如果在这一版本的代码上进行更改,是违背开闭原则的。
从状态不同来看,和之前的“策略模式”很类似,在代码中出现很多if-else,就可以使用策略模式来改善。可以看以下代码,思考一下与策略模式有哪些不同。
//把之前的枚举类型,抽象成了一个抽象基类
//并把之前关于状态的操作,封装到对应的状态对象里
class NetworkState{
public:
//下一个状态
NetworkState* pNext;
//把所有与状态有关的操作,全变成某个状态的行为
virtual void Operation1()=0;
virtual void Operation2()=0;
virtual void Operation3()=0;
virtual ~NetworkState(){}
};
//打开状态
class OpenState :public NetworkState{
static NetworkState* m_instance;
public:
//某个状态没有必要有多个,所以可以使用单例模式
static NetworkState* getInstance(){
if (m_instance == nullptr) {
m_instance = new OpenState();
}
return m_instance;
}
//打开业务执行之后,会转换成另一个状态
void Operation1(){
//**********
pNext = CloseState::getInstance();
}
void Operation2(){
//..........
pNext = ConnectState::getInstance();
}
void Operation3(){
//$$$$$$$$$$
pNext = OpenState::getInstance();
}
};
//关闭状态
class CloseState:public NetworkState{ }
//网络处理者
class NetworkProcessor{
//这里不是存枚举了,而是一个状态对象
NetworkState* pState;
public:
NetworkProcessor(NetworkState* pState){
this->pState = pState;
}
void Operation1(){
//...
//虚函数的本质,其实就是运行时的一个if-else
//如果pState是一个OpenState,那执行的是OpenState的Operator1
pState->Operation1();
pState = pState->pNext; //在状态对象中管理,你下一个状态是谁
//...
}
void Operation2(){
//...
pState->Operation2();
pState = pState->pNext;
//...
}
void Operation3(){
//...
pState->Operation3();
pState = pState->pNext;
//...
}
};
综上,其实是把第一个版本中的状态给抽象出来了:一个状态对象,负责管理它自己的状态与这个状态所要执行的操作。
而NetworkProcessor
中并不涉及具体的状态,而是操作状态的抽象基类!
从而达到一种稳定,如果之后有新的状态过来,添加新的状态即可,不需要更改原先的代码。这就是对扩展开放,对修改关闭。
模式定义
允许一个对象在其内部状态改变时,改变它的行为。从而使对象看起来似乎修改了其行为——《设计模式》GoF。
结构
要点总结
State模式将所有与一个特定状态相关的行为都放入一个State的子类对象中,在对象状态切换时,切换相应的对象;但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦。
为不同的状态引入不同的对象使得状态转换变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换是原子性的——即要么彻底转换过来,要么不转换。
- OpenState只关心三个操作之后,我的下一个状态是什么。不需要想其他情况,这样才不会导致状态转换后的bug。如果像第一个版本中,if-else来执行,那对象转换关系是非常复杂的,很容易写出bug
如果State对象没有实例变量,那么各上下文可以共享同一个State对象,从而节省对象开销。
状态模式与策略模式的区别
在使用设计模式的过程中,你慢慢会发现,很多模式它们之间越来越像、越来越像。这是为什么呢?是因为你看问题的角度多了。
你可以尝试,把它们的类图都找出来,仔细观察它们的相同点和不同点,你会发现它们的相同点越来越多,越来越多。你甚至会把它们具体名字都忘了,不会太计较它们之间的差异性,而是关注它们到底解决了什么稳定点、变化点,以及怎么处理稳定点和变化点之间的关系
慢慢的,你会发现自己不再那么生搬硬套了
- 不再追求去搞清楚这段代码是什么设计模式,这个设计模式是不是这样写的
- 你会发现它们的差异性,叫什么名字,代码怎么写,这些没有那么重要的了。它们无非是设计上的一种演化而已,松耦合设计原则的演化而已
比如状态模式与策略模式,你甚至都可以把它们看成同一种模式。在你眼里它们是一样的,出现了if-else过多的问题,用枚举来解决,就可以转成用多态的方式来实现,在运行时做if-else,而不用在代码里写if-else。你掌握了这个,真的设计模式叫什么,怎么写,怎么实现,都没有这么重要了。