作者:CodePanda_Li 链接:https://www.jianshu.com/p/dbd7a5bc21e9 来源:简书
状态模式(State):当一个对象的内部状态发生改变时,会导致其行为的改变,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。
模式的结构
UML
状态模式.png
在状态模式结构图中包含如下几个角色:
代码示例
当今社会,论坛贴吧很多,我们也会加入感兴趣的论坛,偶尔进行发言,但有时却会发现不能发帖了,原来是昨天的某个帖子引发了口水战,被举报了。这里就用论坛发帖为例,简单用代码描述一下:
帖子代码示例图.png
假设有三种状态,normal(正常),restricted(受限),closed(封号),判断依据是一个健康值(这里只是假设)。
1/* @Time : 2018/8/10 下午3:16
2 **@Author : panda
3 **@Email : codepanda_li@163.com
4 **@File : account.go
5 **@Software: GoLand
6 */
7package account
8
9import "fmt"
10
11type AccountState int
12
13const (
14 NORMAL AccountState = iota //正常0
15 RESTRICTED //受限
16 CLOSED //封号
17)
18
19type Account struct {
20 State AccountState
21 HealthValue int
22}
23
24func NewAccount(health int) *Account {
25 a := &Account{
26 HealthValue: health,
27 }
28 a.changeState()
29 return a
30}
31
32///看帖
33func (a *Account) View() {
34 if a.State == NORMAL || a.State == RESTRICTED {
35 fmt.Println("正常看帖")
36 } else if a.State == CLOSED {
37 fmt.Println("账号被封,无法看帖")
38 }
39
40}
41
42///评论
43func (a *Account) Comment() {
44 if a.State == NORMAL || a.State == RESTRICTED {
45 fmt.Println("正常评论")
46 } else if a.State == CLOSED {
47 fmt.Println("抱歉,你的健康值小于-10,不能评论")
48 }
49
50}
51
52///发帖
53func (a *Account) Post() {
54 if a.State == NORMAL {
55 fmt.Println("正常发帖")
56 } else if a.State == RESTRICTED || a.State == CLOSED {
57 fmt.Println("抱歉,你的健康值小于0,不能发帖")
58 }
59}
60
61func (a *Account) changeState() {
62 if a.HealthValue <= -10 {
63 a.State = CLOSED
64 } else if a.HealthValue > -10 && a.HealthValue <= 0 {
65 a.State = RESTRICTED
66 } else if a.HealthValue > 0 {
67 a.State = NORMAL
68 }
69}
70
71///给账户设定健康值
72func (a *Account) SetHealth(value int) {
73 a.HealthValue = value
74 a.changeState()
75}
上面的代码很简单,能够实现需要的功能,但是却有几个问题:
状态模式可以在一定程度上解决上述问题,在状态模式中将对象在每一个状态下的行为和状态转移语句封装在一个个状态类中,通过这些状态类来分散冗长的条件转移语句,让系统具有更好的灵活性和可扩展性。
1/* @Time : 2018/8/10 下午3:37
2 **@Author : panda
3 **@Email : codepanda_li@163.com
4 **@File : account.go
5 **@Software: GoLand
6 */
7package saccount
8
9import "fmt"
10
11type Account struct {
12 State ActionState
13 HealthValue int
14}
15func NewAccount(health int) *Account {
16 a := &Account{
17 HealthValue: health,
18 }
19 a.changeState()
20 return a
21}
22
23func (a *Account)View() {
24 a.State.View()
25}
26
27func (a *Account)Comment() {
28 a.State.Comment()
29}
30func (a *Account)Post() {
31 a.State.Post()
32}
33type ActionState interface {
34 View()
35 Comment()
36 Post()
37}
38
39type CloseState struct {
40
41}
42
43func (c *CloseState)View() {
44 fmt.Println("账号被封,无法看帖")
45}
46
47func (c *CloseState)Comment() {
48 fmt.Println("抱歉,你的健康值小于-10,不能评论")
49}
50func (c *CloseState)Post() {
51 fmt.Println("抱歉,你的健康值小于0,不能发帖")
52}
53
54type RestrictedState struct {
55
56}
57func (r *RestrictedState)View() {
58 fmt.Println("正常看帖")
59}
60
61func (r *RestrictedState)Comment() {
62 fmt.Println("正常评论")
63}
64func (r *RestrictedState)Post() {
65 fmt.Println("抱歉,你的健康值小于0,不能发帖")
66}
67
68type NormalState struct {
69
70}
71func (n *NormalState)View() {
72 fmt.Println("正常看帖")
73}
74
75func (n *NormalState)Comment() {
76 fmt.Println("正常评论")
77}
78func (n *NormalState)Post() {
79 fmt.Println("正常发帖")
80}
81func (a *Account) changeState() {
82 if a.HealthValue <= -10 {
83 a.State = &CloseState{}
84 } else if a.HealthValue > -10 && a.HealthValue <= 0 {
85 a.State = &RestrictedState{}
86 } else if a.HealthValue > 0 {
87 a.State = &NormalState{}
88 }
89}
90
91///给账户设定健康值
92func (a *Account) SetHealth(value int) {
93 a.HealthValue = value
94 a.changeState()
95}
状态模式的主要优点如下:
状态模式的主要缺点如下:
在以下情况下可以使用状态模式:
状态模式在工作流或游戏等类型的软件中得以广泛使用,甚至可以用于这些系统的核心功能设计,如在政府OA办公系统中,一个批文的状态有多种:尚未办理;正在办理;正在批示;正在审核;已经完成等各种状态,而且批文状态不同时对批文的操作也有所差异。使用状态模式可以描述工作流对象(如批文)的状态转换以及不同状态下它所具有的行为。
说明一下,这个贴子的示例是我印象中看过一个java的对状态模式的实现,觉得很恰当明了,然后自己用golang实现了一遍,现在只有goalng示例代码,忘记了那篇java的出处了。对那个java的作者表示敬意。
版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。