mirror of
https://github.com/crazybber/awesome-patterns.git
synced 2024-11-25 14:26:04 +03:00
state pattern
This commit is contained in:
parent
da2587d3f2
commit
ab8d8e908f
@ -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
|
||||||
|
@ -69,7 +69,7 @@ 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:
|
||||||
/*
|
/*
|
||||||
@ -77,5 +77,6 @@ func main(){
|
|||||||
icg escape
|
icg escape
|
||||||
icg escape
|
icg escape
|
||||||
icg opened fire
|
icg opened fire
|
||||||
|
icg escape
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
@ -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
84
behavioral/state/main.go
Normal 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
118
behavioral/state/problem.go
Normal 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
|
||||||
|
// */
|
||||||
|
//}
|
Loading…
Reference in New Issue
Block a user