Fork me on GitHub

状态模式(State Pattern)

概述:

看看我们平时用的开关,同样一个开关他有2种状态:开和关,当她处于不同的状态的时候她的行为是不一样的,比如当她是开着的时候,你按她一下,她就变成了关闭状态,她是关着的时候按她一下,她就变成了开着的状态。看上去就像是改变了它的类一样,其实我们开发者都知道,我们里面用到了if-else,但是当碰到更多状态时就会造成很多很多if-else,设计和维护就相当的复杂,我们将要学习的状态模式就是允许一个对象在其内部状态改变时改变它的行为,使对象看起来似乎修改了它的类

类图和实例:

上下文环境(Context):它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的Concrete State对象来处理。

抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。

具体状态(Concrete State):实现抽象状态定义的接口。

这里我们举星际争霸里的坦克作为例子,它不架起来的时候可以攻击,可以移动。架起来的时候攻击增强,但是不能移动:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include <iostream>
class SiegeTank;
class ISiegeTankState
{
public:
virtual void move(int x, int y) = 0;
virtual void attack() = 0;
};

class SiegeState : public ISiegeTankState
{
public:
SiegeState(SiegeTank* pTank): m_pTank(pTank){}

virtual void move(int x, int y)
{
std::cout << "Can't move in siege mode." << std::endl;
}

virtual void attack()
{
std::cout << "Attacking for 40" << std::endl;
}

private:
SiegeTank* m_pTank;
};

class TankState : public ISiegeTankState
{
public:
TankState(SiegeTank* pTank): m_pTank(pTank){}

virtual void move(int x, int y)
{
std::cout << "Move to (" << x << ", " << y << ")" << std::endl;
}

virtual void attack()
{
std::cout << "Attacking for 20" << std::endl;
}

private:
SiegeTank* m_pTank;
};

class SiegeTank
{
public:
SiegeTank()
{
m_pTankState = new TankState(this);
m_pSiegeState = new SiegeState(this);
m_pSiegeTankState = m_pTankState;
}

void enterTankMode()
{
m_pSiegeTankState = m_pTankState;
std::cout << "Switch to tank mode" << std::endl;
}

void enterSiegeMode()
{
m_pSiegeTankState = m_pSiegeState;
std::cout << "Switch to siege mode" << std::endl;
}

public:
void attack()
{
m_pSiegeTankState->attack();
}

void move(int x, int y)
{
m_pSiegeTankState->move(x, y);
}

private:
void setState(ISiegeTankState* pSiegeTankMode)
{
m_pSiegeTankState = pSiegeTankMode;
}

private:
TankState* m_pTankState;
SiegeState* m_pSiegeState;
ISiegeTankState* m_pSiegeTankState;
};

int main()
{
SiegeTank tank;
tank.enterTankMode();
tank.attack();
tank.move(1, 1);

tank.enterSiegeMode();
tank.attack();
tank.move(2, 2);

tank.enterTankMode();
tank.attack();
tank.move(3, 3);

return 0;
}

解决的问题:

状态模式主要解决的是当控制一个对象状态装换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简单化。
当一个对象行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。

优缺点:

优点

1,状态模式将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。

2,所有状态相关的代码都存在于某个ConcereteState中,所以通过定义新的子类很容易地增加新的状态和转换。

3,状态模式通过把各种状态转移逻辑分不到State的子类之间,来减少相互间的依赖。

缺点

1,状态模式的使用必然会增加系统类和对象的个数。
2,状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。