本篇介绍了状态模式及相关的面向对象设计原则。状态模式允许对象在内部状态改变时改变它的行为,从而使对象看起来像是修改了它的行为。
1 引例
一些情况下某些对象的状态如果改变,那么它的行为也会随着发生变化。比如一个网络管理器,存在三种状态:网络开启、关闭和连接。通过某些操作能在这些状态间进行转换,但是在不同状态下时,同样的操作导致的结果可能不同(行为发生变化),下面是一个简单的实现,利用枚举存储了三种状态,然后通过最直接的 if-else 来判断当前状态并实现不同的操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| enum NetworkState { Network_Open, Network_Close, Network_Connect, };
class NetworkProcessor { private: 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; } }
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(){ } };
|
按照这种实现方式,当我们要加入新的状态或者新的操作的时候,就大量需要修改类中的代码,当状态复杂的时候很容易就出现各种错误,带来维护问题。
2 状态模式
状态模式将所有的状态打包成一个状态族,每一个具体的状态只负责当前状态下的操作,而原本对象的所有操作都委托给当前的状态对象去进行。对于客户来说,就好像原本的对象在不同状态下改变了它本身的行为,但实际上是通过和状态对象的组合来实现的。
状态模式的类图如下:
形式上与策略模式一模一样,但策略模式是将操作打包成为一族算法,与对象进行组合从而使对象能够切换行为。而状态模式将状态独立出来,使对象可以随着客户的操作自动切换状态并改变行为,而对象本身对此无需知情,只要将对应的操作委托给当前的状态对象去执行即可。
接下来我们将上面的例子使用状态模式进行实现。首先需要实现一个状态接口,其中包含了系统中的所有操作,每个状态都要实现这些操作:
1 2 3 4 5 6 7 8
| class NetworkState { public: NetworkState* pNext; virtual void Operation1()=0; virtual void Operation2()=0; virtual void Operation3()=0; virtual ~NetworkState(){} };
|
然后每一个具体的状态都实现自己的操作,这里以网络开状态为例,其他两个状态同理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class OpenState :public NetworkState{ private: 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(); } };
|
然后网络管理器的代码也需要修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class NetworkProcessor{ private: NetworkState* pState; public: NetworkProcessor(NetworkState* pState) { this->pState = pState; } void Operation1(){ pState->Operation1(); pState = pState->pNext; } void Operation2(){ pState->Operation2(); pState = pState->pNext; } void Operation3(){ pState->Operation3(); pState = pState->pNext; } };
|
这样一来,当新增状态或者操作有变化时,网络管理器对象的代码完全不需要更改,只需修改对应的状态对象的部分就可以了,实现了对象和其状态之间的解耦,或者说实现了具体操作和状态转换之间的解耦。状态模式的另一个潜在好处是,为不同状态引入不同的对象使得状态转换更加明确,可以保证不会出现状态不一致的情况,因为状态的转换是原子的,即要么彻底转换为另一状态,要么不转换。