随心情不定时更新,什么都可能写一点的技术博客

0%

状态模式(State)

官方定义

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

分类

行为型模式

思想

将本来是针对对象的操作,委托给了这个对象的状态操作。运用状态模式可以将代码中的if,else代码块抽离出来,每个if,else下的代码块都封装为状态的某一个方法,所有状态都必须实现状态接口,即实现抽离出来的所有方法,由各自去执行在该状态下应该实现的逻辑,包括不合理的逻辑,可以抛出异常,这样可以规避大量的if,else嵌套。

UML类图

实现

定义状态机Content用来维护自身状态

比如Content代表一个糖果机,它有4种状态:售空未投币已投币售出糖果,我们还可以很容易的为它附加一个中奖状态,稍后就可以看到这比不使用设计模式容易得多,否则会增加很多if,else判断。
糖果机包含5个操作:投币、退币、拉动遥控、出糖果、填充糖果。

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
/// <summary>
/// 糖果机-状态机
/// </summary>
class GumballMachine
{
public State.State soldOutState { get; private set; }
public State.State noQuarterState { get; private set; }
public State.State hasQuarterState { get; private set; }
public State.State soldState { get; private set; }
public State.State winnerState { get; private set; }
public int count { get; private set; }
State.State state;

public GumballMachine(int numberGumballs)
{
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
winnerState = new WinnerState(this);

count = numberGumballs;
state = count > 0 ? noQuarterState : soldOutState;
}

/// <summary>
/// 糖果机投币操作
/// </summary>
public void InsertQuarter()
{
state.InsertQuarter();
}

/// <summary>
/// 糖果机退币操作
/// </summary>
public void EjectQuarter()
{
state.EjectQuarter();
}

/// <summary>
/// 糖果机摇杆操作
/// </summary>
public void TurnCrank()
{
if (state.TurnCrank())
state.Dispense();
}

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

public void ReleaseBall()
{
Console.WriteLine("1颗糖果从卡槽滚了出来..");
count = Math.Max(count - 1, 0);
}

public void Refill(int number)
{
count = number;
state = count > 0 ? noQuarterState : soldOutState;
}

public override string ToString()
{
return string.Format("虚拟糖果机#1001:\n糖果数量:{0}\n当前状态:{1}", count, 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
/// <summary>
/// 对于糖果机的每种状态,都实现4种操作
/// </summary>
abstract class State
{
protected GumballMachine gumballMachine { get; private set; }
public State(GumballMachine gumballMachine)
{
this.gumballMachine = gumballMachine;
}

/// <summary>
/// 投币
/// </summary>
public abstract void InsertQuarter();
/// <summary>
/// 退币
/// </summary>
public abstract void EjectQuarter();
/// <summary>
/// 搬动摇杆
/// </summary>
public abstract bool TurnCrank();
/// <summary>
/// 出糖果
/// </summary>
public abstract void Dispense();
}

为每种状态实现具体的操作

状态下会有很多无效操作,比如未投币就拉摇杆,未使用模式的情况下,我们肯定会在拉摇杆方法中用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
/// <summary>
/// 糖果售完状态
/// </summary>
class SoldOutState : State
{
public SoldOutState(GumballMachine machine) : base(machine)
{

}
public override void Dispense()
{
Console.WriteLine("你必须先投币");
}

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

public override void InsertQuarter()
{
Console.WriteLine("糖果已售空,无法投币");
}

public override bool TurnCrank()
{
Console.WriteLine("你还没有投币,拉动摇杆无反应");
return false;
}

public override string ToString()
{
return "糖果已售空";
}
}

未投币状态

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
/// <summary>
/// 未投币状态
/// </summary>
class NoQuarterState : State
{
public NoQuarterState(GumballMachine machine) : base(machine)
{

}
public override void Dispense()
{
Console.WriteLine("你必须先投币");
}

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

public override void InsertQuarter()
{
Console.WriteLine("你投入了硬币");
gumballMachine.SetState(gumballMachine.hasQuarterState);
}

public override bool TurnCrank()
{
Console.WriteLine("你还没有投币,拉动摇杆无反应");
return false;
}

public override string ToString()
{
return "等待投币";
}
}

投币状态

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
/// <summary>
/// 投完币状态
/// </summary>
class HasQuarterState : State
{
public HasQuarterState(GumballMachine machine) : base(machine)
{

}

public override void Dispense()
{
Console.WriteLine("没有糖果放出来");
}

public override void EjectQuarter()
{
Console.WriteLine("硬币已退出");
gumballMachine.SetState(gumballMachine.noQuarterState);
}

public override void InsertQuarter()
{
Console.WriteLine("你已经投过币了,请不要投2次");
}

/// <summary>
/// 拉动摇杆的时候,10%概率出现中奖状态
/// </summary>
/// <returns></returns>
public override bool TurnCrank()
{
Console.WriteLine("你转动了摇杆..");
Random rd = new Random();
if (rd.Next(10) == 0 && gumballMachine.count > 1)
gumballMachine.SetState(gumballMachine.winnerState);
else
gumballMachine.SetState(gumballMachine.soldState);
return true;
}

public override string ToString()
{
return "机器已投币";
}
}

售出状态

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
/// <summary>
/// 售出状态
/// </summary>
class SoldState : State
{
public SoldState(GumballMachine machine) : base(machine)
{

}
public override void Dispense()
{
gumballMachine.ReleaseBall();
if (gumballMachine.count > 0)
gumballMachine.SetState(gumballMachine.noQuarterState);
else
{
Console.WriteLine("糖果已售空!");
gumballMachine.SetState(gumballMachine.soldOutState);
}
}

public override void EjectQuarter()
{
Console.WriteLine("对不起,你已经拉动摇杆了,无法退币");
}

public override void InsertQuarter()
{
Console.WriteLine("请等待,我们正在为你放出糖果");
}

public override bool TurnCrank()
{
Console.WriteLine("拉动摇杆2次也不会出来第2颗糖果!");
return false;
}

public override string ToString()
{
return "正在出糖果";
}
}

中奖状态

可以出来2颗糖果

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
class WinnerState : State
{
public WinnerState(GumballMachine machine) : base(machine)
{

}
public override void Dispense()
{
Console.WriteLine("恭喜中奖了,你可以获得2颗糖果");
gumballMachine.ReleaseBall();
if (gumballMachine.count > 0)
{
gumballMachine.ReleaseBall();
if (gumballMachine.count > 0)
gumballMachine.SetState(gumballMachine.noQuarterState);
else
{
Console.WriteLine("糖果已售空!");
gumballMachine.SetState(gumballMachine.soldOutState);
}
}
else
{
Console.WriteLine("糖果已售空!");
gumballMachine.SetState(gumballMachine.soldOutState);
}
}

public override void EjectQuarter()
{
Console.WriteLine("对不起,你已经拉动摇杆了,无法退币");
}

public override void InsertQuarter()
{
Console.WriteLine("请等待,我们正在为你放出糖果");
}

public override bool TurnCrank()
{
Console.WriteLine("拉动摇杆2次也不会出来第2颗糖果!");
return false;
}

public override string ToString()
{
return "中奖了,正在出糖果";
}
}

测试用例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static void Main(string[] args)
{
GumballMachine gumballMachine = new GumballMachine(5);

Console.WriteLine(gumballMachine);

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

Console.WriteLine(gumballMachine);

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

Console.WriteLine(gumballMachine);

Console.ReadKey();
}

输出结果