Skip to content

Commit ab8d8e9

Browse files
committed
state pattern
1 parent da2587d commit ab8d8e9

File tree

5 files changed

+225
-8
lines changed

5 files changed

+225
-8
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ A curated collection of idiomatic design & application patterns for Go language.
4242
| [Memento](/behavioral/memento/main.go) | Generate an opaque token that can be used to go back to a previous state ||
4343
| [Observer](/behavioral/observer.md) | Provide a callback for notification of events/changes to data ||
4444
| [Registry](/behavioral/registry.md) | Keep track of all subclasses of a given class ||
45-
| [State](/behavioral/state.md) | Encapsulates varying behavior for the same object based on its internal state | |
45+
| [State](/behavioral/state.md) | Encapsulates varying behavior for the same object based on its internal state | |
4646
| [Strategy](/behavioral/strategy.md) | Enables an algorithm's behavior to be selected at runtime ||
47-
| [Template](/behavioral/template.md) | Defines a skeleton class which defers some methods to subclasses | |
47+
| [Template](/behavioral/template.md) | Defines a skeleton class which defers some methods to subclasses | |
4848
| [Visitor](/behavioral/visitor.md) | Separates an algorithm from an object on which it operates ||
4949

5050
## Synchronization Patterns

behavioral/command/main.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,14 @@ func main(){
6969
attk := CommandAttack{playerA}
7070
escp := CommandEscape{playerA}
7171

72-
invoker.PushCommands(attk ,escp ,escp ,attk)
72+
invoker.PushCommands(attk ,escp ,escp ,attk ,escp)
7373
invoker.CallCommands()
7474
// output:
7575
/*
76-
icg opened fire
77-
icg escape
78-
icg escape
79-
icg opened fire
76+
icg opened fire
77+
icg escape
78+
icg escape
79+
icg opened fire
80+
icg escape
8081
*/
8182
}

behavioral/memento/main.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@ import (
88
"log"
99
)
1010

11+
// originator
1112
type Player struct {
13+
// 需要记录的数据可以考虑单独封装
14+
// type Pos struct{X,Y int}
1215
X,Y int
13-
Name string
16+
1417
// other info
18+
Name string
1519
}
1620

1721
func (p *Player)MoveTo(x,y int){
@@ -31,10 +35,12 @@ func (p *Player)Restore(m PlayerMemento){
3135
p.Y = m.Y
3236
}
3337

38+
// memento
3439
type PlayerMemento struct {
3540
X,Y int
3641
}
3742

43+
// caretaker
3844
type PlayerCareTaker struct {
3945
MementoList *list.List
4046
}
@@ -75,4 +81,12 @@ func main(){
7581

7682
icg.Restore(ct.RemoveLast())
7783
log.Println(icg.X ,icg.Y)
84+
/*
85+
output:
86+
2019/05/02 18:18:03 114 514
87+
2019/05/02 18:18:03 810 19
88+
2019/05/02 18:18:03 0 0
89+
2019/05/02 18:18:03 810 19
90+
2019/05/02 18:18:03 114 514
91+
*/
7892
}

behavioral/state/main.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// 状态模式 state pattern.
2+
// 与策略模式在一些场景可以通用,但策略模式倾向于调用者根据情况手动改变内部策略以切换算法,
3+
// 状态模式倾向于由Context内部自行管理状态,只需要设定初始状态即可,不需要手动切换.
4+
//
5+
// 以下是以跷跷板seesaw为例,分为[左侧高LeftState|右侧高RightState]
6+
package main
7+
8+
import (
9+
"math/rand"
10+
"time"
11+
)
12+
13+
// state
14+
type SeesawState interface {
15+
LiftLeftSide(S *Seesaw)
16+
LiftRightSide(S *Seesaw)
17+
}
18+
19+
type LeftState struct {}
20+
21+
func (LeftState) LiftLeftSide(S *Seesaw) {
22+
println("↑LEFT > left side wads already lifted")
23+
}
24+
25+
func (l LeftState) LiftRightSide(S *Seesaw) {
26+
println("RIGHT↑ > lift right side")
27+
S.State = RightState{}
28+
}
29+
30+
type RightState struct {}
31+
32+
func (r RightState) LiftLeftSide(S *Seesaw) {
33+
println("↑LEFT > lift left side")
34+
S.State = LeftState{}
35+
}
36+
37+
func (RightState) LiftRightSide(S *Seesaw) {
38+
println("RIGHT↑ > right side wads already lifted")
39+
}
40+
41+
// context
42+
type Seesaw struct {
43+
State SeesawState
44+
}
45+
46+
func (s *Seesaw)MakeLeftUp(){
47+
s.State.LiftLeftSide(s)
48+
}
49+
50+
func (s *Seesaw)MakeRightUp(){
51+
s.State.LiftRightSide(s)
52+
}
53+
54+
func main(){
55+
// init left
56+
seesaw := &Seesaw{
57+
State:LeftState{},
58+
}
59+
60+
rand.Seed(time.Now().UnixNano())
61+
for i:=0 ;i<10 ;i++{
62+
if rand.Intn(2) == 1{
63+
// ▄▃▂
64+
seesaw.MakeLeftUp()
65+
}else{
66+
// ▂▃▄
67+
seesaw.MakeRightUp()
68+
}
69+
}
70+
/*
71+
output:
72+
RIGHT↑ > lift right side
73+
RIGHT↑ > right side wads already lifted
74+
RIGHT↑ > right side wads already lifted
75+
↑LEFT > lift left side
76+
↑LEFT > left side wads already lifted
77+
↑LEFT > left side wads already lifted
78+
↑LEFT > left side wads already lifted
79+
RIGHT↑ > lift right side
80+
RIGHT↑ > right side wads already lifted
81+
↑LEFT > lift left side
82+
*/
83+
}
84+

behavioral/state/problem.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// 存疑部分.
2+
//
3+
// 这里是以玩家的健康状态为基准,影响生命回复和受到伤害的例子,有三个状态[健康HealthyState|受伤WoundedState|死亡DeadState].
4+
// 和main.go中的跷跷板例子由行为驱动状态变化不同.以下例子的行为是受到数据影响的,结果就是达不到完全消除if-else的效果
5+
//
6+
// 疑问:
7+
// 1. 很多地方说状态模式可以消除if-else,事实上要做到自行的状态切换很多时候还是会用到if-else,这里的消除应当是指
8+
// 将巨大的条件语句拆分开(?)
9+
// 2. 还是说这种由数据决定状态的事务逻辑下不适合使用状态模式
10+
package main
11+
12+
import (
13+
"errors"
14+
"fmt"
15+
)
16+
17+
// state
18+
type PlayerState interface {
19+
Heal(p *Player) error
20+
Hurt(p *Player ,dmg int)
21+
}
22+
23+
type HealthyState struct {}
24+
25+
func (h HealthyState)Heal(p *Player) error{
26+
return nil
27+
}
28+
29+
func (h HealthyState)Hurt(p *Player ,dmg int){
30+
if dmg > 0 && dmg < p.MaxHealth{
31+
p.Health = p.Health - dmg
32+
p.State = WoundedState{}
33+
}else if dmg > p.MaxHealth{
34+
p.Health = 0
35+
p.State = DeadState{}
36+
}
37+
}
38+
39+
type WoundedState struct {}
40+
41+
func (WoundedState)Heal(p *Player) error{
42+
if p.Health >= p.MaxHealth - 5{
43+
fmt.Printf("healing from %d to %d\n" ,p.Health ,p.MaxHealth)
44+
p.State = HealthyState{}
45+
p.Health = p.MaxHealth
46+
}else{
47+
fmt.Printf("healing from %d to %d\n" ,p.Health ,p.Health+5)
48+
p.Health = p.Health + 5
49+
}
50+
return nil
51+
}
52+
53+
func (h WoundedState)Hurt(p *Player ,dmg int){
54+
if p.Health > dmg{
55+
p.Health = p.Health - dmg
56+
}else {
57+
p.State = DeadState{}
58+
p.Health = 0
59+
}
60+
}
61+
62+
type DeadState struct {}
63+
64+
func (DeadState)Heal(P *Player) error{
65+
return errors.New("you are dead")
66+
}
67+
68+
func (DeadState)Hurt(P *Player ,dmg int){}
69+
70+
// context
71+
type Player struct {
72+
Health int
73+
MaxHealth int
74+
State PlayerState
75+
}
76+
77+
func (p *Player)HealPlayer()error{
78+
return p.State.Heal(p)
79+
}
80+
81+
func (p *Player)HurtPlayer(damage int){
82+
fmt.Printf("damage %d\n" ,damage)
83+
p.State.Hurt(p ,damage)
84+
}
85+
86+
//func main(){
87+
// player := &Player{
88+
// Health:100,
89+
// MaxHealth:100,
90+
// State:HealthyState{},
91+
// }
92+
//
93+
// rand.Seed(time.Now().UnixNano())
94+
// for i:=0 ;i<10 ;i++{
95+
// if err := player.HealPlayer();err != nil{
96+
// fmt.Println(err)
97+
// break
98+
// }
99+
// player.HurtPlayer(rand.Intn(30))
100+
// }
101+
// /*
102+
// output:
103+
// damage 28
104+
// healing from 72 to 77
105+
// damage 29
106+
// healing from 48 to 53
107+
// damage 15
108+
// healing from 38 to 43
109+
// damage 1
110+
// healing from 42 to 47
111+
// damage 19
112+
// healing from 28 to 33
113+
// damage 27
114+
// healing from 6 to 11
115+
// damage 16
116+
// you are dead
117+
// */
118+
//}

0 commit comments

Comments
 (0)