mirror of
https://github.com/crazybber/awesome-patterns.git
synced 2024-11-21 20:36:01 +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 | ✔ |
|
||||
| [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 | ✘ |
|
||||
| [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 | ✔ |
|
||||
| [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 | ✘ |
|
||||
|
||||
## Synchronization Patterns
|
||||
|
@ -69,13 +69,14 @@ func main(){
|
||||
attk := CommandAttack{playerA}
|
||||
escp := CommandEscape{playerA}
|
||||
|
||||
invoker.PushCommands(attk ,escp ,escp ,attk)
|
||||
invoker.PushCommands(attk ,escp ,escp ,attk ,escp)
|
||||
invoker.CallCommands()
|
||||
// output:
|
||||
/*
|
||||
icg opened fire
|
||||
icg escape
|
||||
icg escape
|
||||
icg opened fire
|
||||
icg opened fire
|
||||
icg escape
|
||||
icg escape
|
||||
icg opened fire
|
||||
icg escape
|
||||
*/
|
||||
}
|
||||
|
@ -8,10 +8,14 @@ import (
|
||||
"log"
|
||||
)
|
||||
|
||||
// originator
|
||||
type Player struct {
|
||||
// 需要记录的数据可以考虑单独封装
|
||||
// type Pos struct{X,Y int}
|
||||
X,Y int
|
||||
Name string
|
||||
|
||||
// other info
|
||||
Name string
|
||||
}
|
||||
|
||||
func (p *Player)MoveTo(x,y int){
|
||||
@ -31,10 +35,12 @@ func (p *Player)Restore(m PlayerMemento){
|
||||
p.Y = m.Y
|
||||
}
|
||||
|
||||
// memento
|
||||
type PlayerMemento struct {
|
||||
X,Y int
|
||||
}
|
||||
|
||||
// caretaker
|
||||
type PlayerCareTaker struct {
|
||||
MementoList *list.List
|
||||
}
|
||||
@ -75,4 +81,12 @@ func main(){
|
||||
|
||||
icg.Restore(ct.RemoveLast())
|
||||
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