diff --git a/README.md b/README.md index 9728491..af55497 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,9 @@ + [x] [模板方法模式(Template Method)](./behavior/05_template_method) + [x] [策略模式(Strategy)](./behavior/12_strategy) + [ ] [WIP][状态模式(State)](./behavior/behavior16_state) -+ [ ] [WIP][备忘录模式(Memento)](./behavior/09_memento) ++ [ ] [备忘录模式(Memento)](./behavior/09_memento) + [x] [访问者模式(Visitor)](./behavior/07_visitor) -+ [ ] [WIP][解释器模式(Interpreter)](./behavior/08_interpreter) ++ [x] [解释器模式(Interpreter)](./behavior/08_interpreter) + [x] [职责链模式(Chain of Responsibility)](./behavior/06_chain_of_responsibility) diff --git a/behavior/09_memento/README.md b/behavior/09_memento/README.md index e5934ab..071b4f8 100644 --- a/behavior/09_memento/README.md +++ b/behavior/09_memento/README.md @@ -1,7 +1,16 @@ # 备忘录模式 -备忘录模式用于保存程序内部状态到外部,又不希望暴露内部状态的情形。 +备忘录模式,又叫快照模式,备份模式,用于在不暴露内部状态的情况下,保存程序内部状态到外部。 -程序内部状态使用窄接口船体给外部进行存储,从而不暴露程序实现细节。 +主要保存的是数据(也就是状态),这些数据可以是静态数据,也可以是一个操作描述。 -备忘录模式同时可以离线保存内部状态,如保存到数据库,文件等。 +在CQRS中的事件溯源模式(Event Sourcing)中,就是保存事件的操作(包含操作参数集)到持久化数据中,以达到事件回溯的目的. + +现实生活中涉及备份和快照的例子就太多了,日常的打游戏存档,下次玩的时候,读取存档,读取进度,也是这个模式。 + + +该模式中有三个关键角色: + + 1. 发起人角色 Originator: 负责记录当前的内部状态,提供当前状态数据,并负责恢复备忘录数据。 + 2. 备忘录角色 Memento : 负责存放发起人对象某个时刻的内部状态,这就是要保存的数据结构类型。 + 3. 管理者角色 Caretaker: 负责保存备忘录对象。 diff --git a/behavior/09_memento/memento.go b/behavior/09_memento/memento.go index 655bf7d..3548705 100644 --- a/behavior/09_memento/memento.go +++ b/behavior/09_memento/memento.go @@ -1,35 +1,65 @@ package memento -import "fmt" +import ( + "fmt" + "time" +) -type Memento interface{} +//////////////////////////////// +//使用游戏玩家的角色存档和读取的例子 -type Game struct { - hp, mp int +//GamePlayer 是一个Originator 提供当前的游戏状态 +type GamePlayer struct { + hp, mp, role, level int //血量,魔法值,当前关卡 } -type gameMemento struct { - hp, mp int +//RoleStatusMemento 一条备忘数据,存放瞬时状态的数据结构,一个数据结构 +type RoleStatusMemento struct { + tag string //存档记录本身的名称,以便下次识别读取 + hp, mp, level int //血量,魔法值,角色类型,当前关卡, + timeMark string //存档的可视化时间 } -func (g *Game) Play(mpDelta, hpDelta int) { - g.mp += mpDelta - g.hp += hpDelta +//RoleStatusCaretaker 负责保存角色当前的状态数据,提供存取能力 +//RoleStatusCaretaker 也是占内存/存储的地方,如果不停的读取,IO压力会变大的很大 +type RoleStatusCaretaker struct { + memens map[string]*RoleStatusMemento } -func (g *Game) Save() Memento { - return &gameMemento{ - hp: g.hp, - mp: g.mp, +//SaveStatus 保存当前角色的游戏状态 +func (r *RoleStatusCaretaker) SaveStatus(item *RoleStatusMemento) { + r.memens[item.tag] = item + fmt.Printf("Game File %s Saved at %s\n", item.tag, item.timeMark) +} + +//RetriveStatus 提供需要的状态 +func (r *RoleStatusCaretaker) RetriveStatus(savedTag string) *RoleStatusMemento { + return r.memens[savedTag] + +} + +//Create 创建游戏的当前档案存档 +func (g *GamePlayer) Create(tagName string) *RoleStatusMemento { + + return &RoleStatusMemento{ + tag: tagName, + hp: g.hp, + mp: g.mp, + level: g.level, + timeMark: time.Now().String(), } } -func (g *Game) Load(m Memento) { - gm := m.(*gameMemento) - g.mp = gm.mp - g.hp = gm.hp +//Load 载入存档,恢复数据 +func (g *GamePlayer) Load(rm *RoleStatusMemento) { + g.mp = rm.mp + g.hp = rm.hp + g.level = rm.level + + fmt.Printf("Game Profile had been restored to %s : %s\n", rm.tag, rm.timeMark) } -func (g *Game) Status() { - fmt.Printf("Current HP:%d, MP:%d\n", g.hp, g.mp) +//Status 玩家角色的当前状态 +func (g *GamePlayer) Status() { + fmt.Printf("Current Level :%d HP:%d, MP:%d\n", g.level, g.hp, g.mp) } diff --git a/behavior/09_memento/memento_test.go b/behavior/09_memento/memento_test.go index e19d5db..f96ff46 100644 --- a/behavior/09_memento/memento_test.go +++ b/behavior/09_memento/memento_test.go @@ -1,22 +1,44 @@ package memento -func ExampleGame() { - game := &Game{ - hp: 10, - mp: 10, - } +import ( + "testing" + "time" +) - game.Status() - progress := game.Save() +func TestGameArchive(t *testing.T) { - game.Play(-2, -3) - game.Status() + gamerole := GamePlayer{hp: 1000, mp: 232, level: 20} - game.Load(progress) - game.Status() + datakeeper := RoleStatusCaretaker{memens: make(map[string]*RoleStatusMemento)} + + archive1 := gamerole.Create("第一次存档") + + //交给管数据的人,存起来 + datakeeper.SaveStatus(archive1) + + //模拟,随机玩会儿游戏 + time.Sleep(time.Millisecond * 1132) + + //更新角色当前状态 + gamerole = GamePlayer{hp: 500, mp: 10, level: 30} + + //看一下状态 + gamerole.Status() + + archive2 := gamerole.Create("第二次存档") + + //交给管数据的人,存起来 + datakeeper.SaveStatus(archive2) + + //准备恢复第一次的存档 + + //查找档案 + restore1 := datakeeper.RetriveStatus("第一次存档") + + //载入档案 + gamerole.Load(restore1) + + //看一下状态 + gamerole.Status() - // Output: - // Current HP:10, MP:10 - // Current HP:7, MP:8 - // Current HP:10, MP:10 }