0%

【设计模式】状态模式

本篇介绍了状态模式及相关的面向对象设计原则。状态模式允许对象在内部状态改变时改变它的行为,从而使对象看起来像是修改了它的行为。

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 状态模式

状态模式将所有的状态打包成一个状态族,每一个具体的状态只负责当前状态下的操作,而原本对象的所有操作都委托给当前的状态对象去进行。对于客户来说,就好像原本的对象在不同状态下改变了它本身的行为,但实际上是通过和状态对象的组合来实现的。

状态模式的类图如下:
image-20230307134445001

形式上与策略模式一模一样,但策略模式是将操作打包成为一族算法,与对象进行组合从而使对象能够切换行为。而状态模式将状态独立出来,使对象可以随着客户的操作自动切换状态并改变行为,而对象本身对此无需知情,只要将对应的操作委托给当前的状态对象去执行即可。

接下来我们将上面的例子使用状态模式进行实现。首先需要实现一个状态接口,其中包含了系统中的所有操作,每个状态都要实现这些操作:

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;
}
};

这样一来,当新增状态或者操作有变化时,网络管理器对象的代码完全不需要更改,只需修改对应的状态对象的部分就可以了,实现了对象和其状态之间的解耦,或者说实现了具体操作和状态转换之间的解耦。状态模式的另一个潜在好处是,为不同状态引入不同的对象使得状态转换更加明确,可以保证不会出现状态不一致的情况,因为状态的转换是原子的,即要么彻底转换为另一状态,要么不转换。

---- 本文结束 知识又增加了亿点点!----

文章版权声明 1、博客名称:LycTechStack
2、博客网址:https://lz328.github.io/LycTechStack.github.io/
3、本博客的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系博主进行删除处理。
4、本博客所有文章版权归博主所有,如需转载请标明出处。