state pattern

This commit is contained in:
nynicg 2019-05-03 10:48:46 +08:00
parent da2587d3f2
commit ab8d8e908f
5 changed files with 225 additions and 8 deletions

View File

@ -42,9 +42,9 @@ A curated collection of idiomatic design & application patterns for Go language.
| [Memento](/behavioral/memento/main.go) | Generate an opaque token that can be used to go back to a previous state | ✔ | | [Memento](/behavioral/memento/main.go) | Generate an opaque token that can be used to go back to a previous state | ✔ |
| [Observer](/behavioral/observer.md) | Provide a callback for notification of events/changes to data | ✔ | | [Observer](/behavioral/observer.md) | Provide a callback for notification of events/changes to data | ✔ |
| [Registry](/behavioral/registry.md) | Keep track of all subclasses of a given class | ✘ | | [Registry](/behavioral/registry.md) | Keep track of all subclasses of a given class | ✘ |
| [State](/behavioral/state.md) | Encapsulates varying behavior for the same object based on its internal state | | | [State](/behavioral/state.md) | Encapsulates varying behavior for the same object based on its internal state | |
| [Strategy](/behavioral/strategy.md) | Enables an algorithm's behavior to be selected at runtime | ✔ | | [Strategy](/behavioral/strategy.md) | Enables an algorithm's behavior to be selected at runtime | ✔ |
| [Template](/behavioral/template.md) | Defines a skeleton class which defers some methods to subclasses | | | [Template](/behavioral/template.md) | Defines a skeleton class which defers some methods to subclasses | |
| [Visitor](/behavioral/visitor.md) | Separates an algorithm from an object on which it operates | ✘ | | [Visitor](/behavioral/visitor.md) | Separates an algorithm from an object on which it operates | ✘ |
## Synchronization Patterns ## Synchronization Patterns

View File

@ -69,13 +69,14 @@ func main(){
attk := CommandAttack{playerA} attk := CommandAttack{playerA}
escp := CommandEscape{playerA} escp := CommandEscape{playerA}
invoker.PushCommands(attk ,escp ,escp ,attk) invoker.PushCommands(attk ,escp ,escp ,attk ,escp)
invoker.CallCommands() invoker.CallCommands()
// output: // output:
/* /*
icg opened fire icg opened fire
icg escape icg escape
icg escape icg escape
icg opened fire icg opened fire
icg escape
*/ */
} }

View File

@ -8,10 +8,14 @@ import (
"log" "log"
) )
// originator
type Player struct { type Player struct {
// 需要记录的数据可以考虑单独封装
// type Pos struct{X,Y int}
X,Y int X,Y int
Name string
// other info // other info
Name string
} }
func (p *Player)MoveTo(x,y int){ func (p *Player)MoveTo(x,y int){
@ -31,10 +35,12 @@ func (p *Player)Restore(m PlayerMemento){
p.Y = m.Y p.Y = m.Y
} }
// memento
type PlayerMemento struct { type PlayerMemento struct {
X,Y int X,Y int
} }
// caretaker
type PlayerCareTaker struct { type PlayerCareTaker struct {
MementoList *list.List MementoList *list.List
} }
@ -75,4 +81,12 @@ func main(){
icg.Restore(ct.RemoveLast()) icg.Restore(ct.RemoveLast())
log.Println(icg.X ,icg.Y) log.Println(icg.X ,icg.Y)
/*
output:
2019/05/02 18:18:03 114 514
2019/05/02 18:18:03 810 19
2019/05/02 18:18:03 0 0
2019/05/02 18:18:03 810 19
2019/05/02 18:18:03 114 514
*/
} }

84
behavioral/state/main.go Normal file
View File

@ -0,0 +1,84 @@
// 状态模式 state pattern.
// 与策略模式在一些场景可以通用,但策略模式倾向于调用者根据情况手动改变内部策略以切换算法,
// 状态模式倾向于由Context内部自行管理状态只需要设定初始状态即可不需要手动切换.
//
// 以下是以跷跷板seesaw为例分为[左侧高LeftState|右侧高RightState]
package main
import (
"math/rand"
"time"
)
// state
type SeesawState interface {
LiftLeftSide(S *Seesaw)
LiftRightSide(S *Seesaw)
}
type LeftState struct {}
func (LeftState) LiftLeftSide(S *Seesaw) {
println("↑LEFT > left side wads already lifted")
}
func (l LeftState) LiftRightSide(S *Seesaw) {
println("RIGHT↑ > lift right side")
S.State = RightState{}
}
type RightState struct {}
func (r RightState) LiftLeftSide(S *Seesaw) {
println("↑LEFT > lift left side")
S.State = LeftState{}
}
func (RightState) LiftRightSide(S *Seesaw) {
println("RIGHT↑ > right side wads already lifted")
}
// context
type Seesaw struct {
State SeesawState
}
func (s *Seesaw)MakeLeftUp(){
s.State.LiftLeftSide(s)
}
func (s *Seesaw)MakeRightUp(){
s.State.LiftRightSide(s)
}
func main(){
// init left
seesaw := &Seesaw{
State:LeftState{},
}
rand.Seed(time.Now().UnixNano())
for i:=0 ;i<10 ;i++{
if rand.Intn(2) == 1{
// ▄▃▂
seesaw.MakeLeftUp()
}else{
// ▂▃▄
seesaw.MakeRightUp()
}
}
/*
output:
RIGHT > lift right side
RIGHT > right side wads already lifted
RIGHT > right side wads already lifted
LEFT > lift left side
LEFT > left side wads already lifted
LEFT > left side wads already lifted
LEFT > left side wads already lifted
RIGHT > lift right side
RIGHT > right side wads already lifted
LEFT > lift left side
*/
}

118
behavioral/state/problem.go Normal file
View File

@ -0,0 +1,118 @@
// 存疑部分.
//
// 这里是以玩家的健康状态为基准,影响生命回复和受到伤害的例子,有三个状态[健康HealthyState|受伤WoundedState|死亡DeadState].
// 和main.go中的跷跷板例子由行为驱动状态变化不同.以下例子的行为是受到数据影响的,结果就是达不到完全消除if-else的效果
//
// 疑问:
// 1. 很多地方说状态模式可以消除if-else事实上要做到自行的状态切换很多时候还是会用到if-else这里的消除应当是指
// 将巨大的条件语句拆分开(?)
// 2. 还是说这种由数据决定状态的事务逻辑下不适合使用状态模式
package main
import (
"errors"
"fmt"
)
// state
type PlayerState interface {
Heal(p *Player) error
Hurt(p *Player ,dmg int)
}
type HealthyState struct {}
func (h HealthyState)Heal(p *Player) error{
return nil
}
func (h HealthyState)Hurt(p *Player ,dmg int){
if dmg > 0 && dmg < p.MaxHealth{
p.Health = p.Health - dmg
p.State = WoundedState{}
}else if dmg > p.MaxHealth{
p.Health = 0
p.State = DeadState{}
}
}
type WoundedState struct {}
func (WoundedState)Heal(p *Player) error{
if p.Health >= p.MaxHealth - 5{
fmt.Printf("healing from %d to %d\n" ,p.Health ,p.MaxHealth)
p.State = HealthyState{}
p.Health = p.MaxHealth
}else{
fmt.Printf("healing from %d to %d\n" ,p.Health ,p.Health+5)
p.Health = p.Health + 5
}
return nil
}
func (h WoundedState)Hurt(p *Player ,dmg int){
if p.Health > dmg{
p.Health = p.Health - dmg
}else {
p.State = DeadState{}
p.Health = 0
}
}
type DeadState struct {}
func (DeadState)Heal(P *Player) error{
return errors.New("you are dead")
}
func (DeadState)Hurt(P *Player ,dmg int){}
// context
type Player struct {
Health int
MaxHealth int
State PlayerState
}
func (p *Player)HealPlayer()error{
return p.State.Heal(p)
}
func (p *Player)HurtPlayer(damage int){
fmt.Printf("damage %d\n" ,damage)
p.State.Hurt(p ,damage)
}
//func main(){
// player := &Player{
// Health:100,
// MaxHealth:100,
// State:HealthyState{},
// }
//
// rand.Seed(time.Now().UnixNano())
// for i:=0 ;i<10 ;i++{
// if err := player.HealPlayer();err != nil{
// fmt.Println(err)
// break
// }
// player.HurtPlayer(rand.Intn(30))
// }
// /*
// output:
// damage 28
// healing from 72 to 77
// damage 29
// healing from 48 to 53
// damage 15
// healing from 38 to 43
// damage 1
// healing from 42 to 47
// damage 19
// healing from 28 to 33
// damage 27
// healing from 6 to 11
// damage 16
// you are dead
// */
//}