0%

状态模式(State)

引子

设计模式的六大原则

状态模式(State)

在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。

在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。

主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。

何时使用:代码中包含大量与对象状态有关的条件语句。

如何解决:将各种具体的状态类抽象出来。

关键代码:通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if…else 等条件选择语句。

定义与特点

定义

允许一个对象在其内部状态发生改变时改变其行为能力。

主要优点

1、封装了转换规则。
2、枚举可能的状态,在枚举状态之前需要确定状态种类。
3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

主要缺点

1、状态模式的使用必然会增加系统类和对象的个数。
2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
3、状态模式对”开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

结构与实现

结构

角色有哪些?

  1. 抽象状态接口
  2. 实体状态类
  3. 带状态的类

设计图

实现

写实现代码

糖果机

实现4个操作

  1. 投币
  2. 退币
  3. 抽糖果
  4. 发糖

有4个状态

  • 没有硬币状态
  • 投币状态
  • 出售糖果状态
  • 糖果售尽状态

在不适用状态模式下代码

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
using System;

namespace StatePattern
{
/// <summary>
/// 糖果机
/// 未使用设计模式
/// </summary>
class NoPatternGumballMachine
{
/*
* 四个状态
*/
/**没有硬币状态*/
private const int NO_QUARTER = 0;
/**投币状态*/
private const int HAS_QUARTER = 1;
/**出售糖果状态*/
private const int SOLD = 2;
/**糖果售尽状态*/
private const int SOLD_OUT = 3;

private int state = SOLD_OUT;
private int candyCount = 0;

public NoPatternGumballMachine(int count)
{
this.candyCount = count;
if (candyCount > 0)
state = NO_QUARTER;
}

/*
* 四个动作
*/

/**
* 投币
*/
public void insertQuarter()
{
if (NO_QUARTER == state)
{
Console.WriteLine("投币");
state = HAS_QUARTER;
}
else if (HAS_QUARTER == state)
{
Console.WriteLine("请不要重复投币!");
returnQuarter();
}
else if (SOLD == state)
{
Console.WriteLine("已投币,请等待糖果");
returnQuarter();
}
else if (SOLD_OUT == state)
{
Console.WriteLine("糖果已经售尽");
returnQuarter();
}
}

/**
* 退币
*/
public void ejectQuarter()
{
if (NO_QUARTER == state)
{
Console.WriteLine("没有硬币,无法弹出");
}
else if (HAS_QUARTER == state)
{
returnQuarter();
state = NO_QUARTER;
}
else if (SOLD == state)
{
Console.WriteLine("无法退币,正在发放糖果,请等待");
}
else if (SOLD_OUT == state)
{
Console.WriteLine("没有投币,无法退币");
}
}

/**
* 转动出糖曲轴
*/
public void turnCrank()
{
if (NO_QUARTER == state)
{
Console.WriteLine("请先投币");
}
else if (HAS_QUARTER == state)
{
Console.WriteLine("转动曲轴,准备发糖");
state = SOLD;
}
else if (SOLD == state)
{
Console.WriteLine("已按过曲轴,请等待");
}
else if (SOLD_OUT == state)
{
Console.WriteLine("糖果已经售尽");
}
}

/**
* 发糖
*/
public void dispense()
{
if (NO_QUARTER == state)
{
Console.WriteLine("没有投币,无法发放糖果");
}
else if (HAS_QUARTER == state)
{
Console.WriteLine("this method don't support");
}
else if (SOLD == state)
{
if (candyCount > 0)
{
Console.WriteLine("分发一颗糖果");
candyCount--;
state = NO_QUARTER;
}
else
{
Console.WriteLine("抱歉,糖果已售尽");
state = SOLD_OUT;
}
}
else if (SOLD_OUT == state)
{
Console.WriteLine("抱歉,糖果已售尽");
}
}

/**
* 退还硬币
*/
protected void returnQuarter()
{
Console.WriteLine("退币……");
}
}
}

使用状态模式

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
using System;

namespace StatePattern
{
/// <summary>
/// 抽象状态接口
/// </summary>
abstract class State
{
/**
* 投币
*/
public abstract void InsertQuarter();
/**
* 退币
*/
public abstract void EjectQuarter();
/**
* 转动出糖曲轴
*/
public abstract void TurnCrank();
/**
* 发糖
*/
public abstract void Dispense();
/**
* 退还硬币
*/
protected void ReturnQuarter()
{
Console.WriteLine("退币……");
}
}
}

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StatePattern
{
/// <summary>
/// 没有硬币的状态
/// </summary>
class NoQuarterState : State
{
private readonly GumballMachine gumballMachine;

public NoQuarterState(GumballMachine gumballMachine)
{
this.gumballMachine = gumballMachine;
}

public override void Dispense()
{
Console.WriteLine("没有投币,无法发放糖果");
}

public override void EjectQuarter()
{
Console.WriteLine("没有硬币,无法弹出");
}

public override void InsertQuarter()
{
Console.WriteLine("你投入了一个硬币");
//转换为有硬币状态
gumballMachine.SetState(gumballMachine.hasQuarterState);
}

public override void TurnCrank()
{
Console.WriteLine("请先投币");
}
}
}

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
using System;

namespace StatePattern
{
/**
* 投硬币的状态
*/
class HasQuarterState : State
{
private GumballMachine gumballMachine;

public HasQuarterState(GumballMachine gumballMachine)
{
this.gumballMachine = gumballMachine;
}

public override void Dispense()
{
Console.WriteLine("this method don't support");
}

public override void EjectQuarter()
{
ReturnQuarter();
gumballMachine.SetState(gumballMachine.noQuarterState);
}

public override void InsertQuarter()
{
Console.WriteLine("请不要重复投币!");
ReturnQuarter();
}

public override void TurnCrank()
{
Console.WriteLine("转动曲轴,准备发糖");
gumballMachine.SetState(gumballMachine.soldState);
}
}
}
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StatePattern
{
class SoldState : State
{
private GumballMachine gumballMachine;

public SoldState(GumballMachine gumballMachine)
{
this.gumballMachine = gumballMachine;
}

public override void Dispense()
{
int candyCount = gumballMachine.GetCandyCount();
if (candyCount > 0)
{
Console.WriteLine("分发一颗糖果");
candyCount--;
gumballMachine.SetCandyCount(candyCount);
if (candyCount > 0)
{
gumballMachine.SetState(gumballMachine.noQuarterState);
return;
}
}
Console.WriteLine("抱歉,糖果已售尽");
gumballMachine.SetState(gumballMachine.soldOutState);
}

public override void EjectQuarter()
{
Console.WriteLine("无法退币,正在发放糖果,请等待");
}

public override void InsertQuarter()
{
Console.WriteLine("已投币,请等待糖果");
ReturnQuarter();
}

public override void TurnCrank()
{
Console.WriteLine("已按过曲轴,请等待");
}
}
}
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
using System;

namespace StatePattern
{
class SoldOutState : State
{
private GumballMachine gumballMachine;

public SoldOutState(GumballMachine gumballMachine)
{
this.gumballMachine = gumballMachine;
}

public override void Dispense()
{
Console.WriteLine("糖果已经售尽");
}

public override void EjectQuarter()
{
Console.WriteLine("没有投币,无法退币");
}

public override void InsertQuarter()
{
Console.WriteLine("糖果已经售尽");
ReturnQuarter();
}

public override void TurnCrank()
{
Console.WriteLine("糖果已经售尽");
}
}
}
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
namespace StatePattern
{
class GumballMachine : State
{
public State noQuarterState;
public State hasQuarterState;
public State soldState;
public State soldOutState;

private State state;
private int candyCount = 0;

public GumballMachine(int count)
{
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
state = soldOutState = new SoldOutState(this);

this.candyCount = count;
if (count > 0)
SetState(noQuarterState);
}

public void SetState(State state)
{
this.state = state;
}

public State GetState()
{
return state;
}

public void SetCandyCount(int candyCount)
{
this.candyCount = candyCount;
}

public int GetCandyCount()
{
return candyCount;
}

public override void Dispense()
{
state.Dispense();
}

public override void EjectQuarter()
{
state.EjectQuarter();
}

public override void InsertQuarter()
{
state.InsertQuarter();
}

public override void TurnCrank()
{
state.TurnCrank();
}
}
}

测试

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StatePattern
{
class Program
{
static void Main(string[] args)
{
NoPatternGumballMachine noPatternGumballMachine = new NoPatternGumballMachine(10);

noPatternGumballMachine.insertQuarter();
noPatternGumballMachine.turnCrank();
noPatternGumballMachine.dispense();
noPatternGumballMachine.dispense();

GumballMachine gumballMachine = new GumballMachine(10);

gumballMachine.InsertQuarter();
gumballMachine.TurnCrank();
gumballMachine.Dispense();
gumballMachine.Dispense();


Console.ReadLine();
}
}
}

应用场景

1、行为随状态改变而改变的场景。
2、条件、分支语句的代替者。

注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。

扩展

欢迎关注我的其它发布渠道