状态模式
首先允许我吐槽一下,原计划周一、三、五更新文章的。刚计划完就被安排了工作任务,而设计模式系列文章更新的流程是学习->准备代码案例->整理出文章。然后我毫无意外的脱更了。。。周六日要录制视频,所以由原来的一周三更调整为一周一更
今天要聊的是状态设计模式,可能看名字根本不知道有什么用,我们还是老规矩,以案例代入
需求描述
月有阴晴圆缺、天气有晴阴雨雪,而这些都是状态,现在我们以天气为例,用代码的形式表示不同的天气信息
# 需求实现(非设计模式)
var WeatherState := 1;
case WeatherState of
1:
Writeln('晴');
2:
Writeln('阴');
3:
Writeln('雨');
4:
Writeln('雪');
end;
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
上述代码其实没有特别需要说明的,因为足够简单。以变量 WeatherState 为判断依据,分别输出不同的天气状态信息。但是,这个天气的状态信息足够简单所以并不显的我们的代码有什么问题,但是在实际生活中每一种天气都需要有其他的一些信息组成,例如:风向、风力、温度等等,当我们把这些信息组装起来之后你会发现使用 case 语句来完成这个功能会让代码看上去无比的臃肿。记得我初学编程的时候听前辈针对 case 语句说过一个评价 “新手专用,大神远离”
那么问题来了,不使用 case 语句我们怎么实现?这就是我们今天要聊的设计模式
# 需求实现(设计模式)
这一次我放了全部的代码,因为我发现我翻看以前的笔记的时候只看代码片段死活想不起来怎么使用了。。。
unit UnitState;
interface
type
// 抽象状态类:用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为
TWeatherState = class
public
//获取天气状态的信息
function GetWeatherMessage(): string; virtual; abstract;
end;
// 实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
//具体状态类下雨
TRain = class(TWeatherState)
public
function GetWeatherMessage(): string; override;
end;
//具体状态类晴天
TSunshine = class(TWeatherState)
public
function GetWeatherMessage(): string; override;
end;
TContext = class
//环境类:定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
private
FWeather: TWeatherState;
public
//切换状态
property CurrentWeather: TWeatherState read FWeather write FWeather;
//获取天气信息
function GetWeather(): string;
//重写构造
constructor Create(); overload;
end;
implementation
{ TRain }
function TRain.GetWeatherMessage: string;
begin
Result := '有雨';
end;
{ TSunshine }
function TSunshine.GetWeatherMessage: string;
begin
Result := '晴天';
end;
{ TContext }
constructor TContext.Create;
begin
//默认维护的状态
Self.CurrentWeather := TRain.Create();
end;
function TContext.GetWeather: string;
begin
Result := CurrentWeather.GetWeatherMessage();
end;
end.
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
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
调用
uses
System.SysUtils,
UnitState in 'UnitState.pas';
begin
try
var Context := TContext.Create();
//获取默认状态
Writeln(Context.GetWeather());
//切换状态
Context.CurrentWeather := TSunshine.Create();
//获取切换后的状态
Writeln(Context.GetWeather());
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 小结一下
通过设计模式的封装,调用的代码确实变的简单了,只是它也有缺点,其优缺点总结如下
优点:
结构清晰,避免了过多的 case 或 if…else 语句的使用
封装性非常好,状态变化放置到了类的内部来实现,外部调用不需要知道类内部如何实现状态和行为的变换
很好的体现了开闭原则和单一职责原则,想要增加状态就增加子类,想要修改状态就修改子类即可
缺点:
- 子类会太多,也即类膨胀
至此总结完毕,最后我想说一下,我新开了一个公众号主要是把 Delphi 和 Java 区分开,感兴趣的朋友可以关注一波!!!