Go设计模式(25)-备忘录模式

简介: 当初学备忘录模式的时候,特别开心。这不就是游戏里的备份嘛!游戏关闭之后,重新开启,从上次结束的位置继续开始。但终归没有进入游戏行业,也没有机会用过备忘录模式。

当初学备忘录模式的时候,特别开心。这不就是游戏里的备份嘛!游戏关闭之后,重新开启,从上次结束的位置继续开始。但终归没有进入游戏行业,也没有机会用过备忘录模式。

UML类图位置:https://www.processon.com/view/link/60d29bf3e401fd49502afd25

本文代码链接为:https://github.com/shidawuhen/asap/blob/master/controller/design/25Memento.go

1.定义

1.1 备忘录模式

备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

UML:

图片

1.2分析

从定义上看,除了不破坏封装性外,其它都比较容易理解。对于不破坏封装性,我觉得有两点:

  1. 备忘录是Originator自己创建的
  2. 备忘录应该只有获取相关的接口,不应该有修改相关的接口

Caretaker是做什么用的呢?备忘录不是只备忘一份,可能备忘多份,Caretaker就是管理众多备忘录的。

Originator通过CreateMemento创建备忘录,通过SetMemento恢复到指定状态。

为什么备份的时候使用的是Memento而不是直接使用Originator呢?这是因为Memento只保存数据,如果将Originator保存,则表示将功能也进行保存,属于不该保存的而保存了。

另外只保存数据还有一个好处,即使解析出数据,也不知道如何使用,只有Originator知道真正的口诀。

2.应用场景

游戏或者文档,经常使用到备份相关的功能。玩游戏打Boss的时候,一般会做存档,失败了重新来。文档也有回滚功能,否则毕业论文写着写着断电了,所有内容化为乌有,多惨。

以前写过Go设计模式(22)-状态模式,这也算一个简单的小游戏,正好用它为基础实现一个备忘录模式。

3.代码实现

package main

import (
   "container/list"
   "fmt"
)

/**
 * @Author: Jason Pang
 * @Description: 备忘录
 */
type Memento struct {
   mario *Mario
}

func (m *Memento) GetMario() *Mario {
   return m.mario
}

/**
 * @Author: Jason Pang
 * @Description: 管理备忘录
 */
type Caretaker struct {
   stack *list.List
}

/**
 * @Author: Jason Pang
 * @Description: 保存备忘录
 * @receiver c
 * @param m
 */
func (c *Caretaker) Save(m *Memento) {
   c.stack.PushBack(m)
}

/**
 * @Author: Jason Pang
 * @Description: 获取上一个备忘录
 * @receiver c
 * @return *Memento
 */
func (c *Caretaker) Pop() *Memento {
   e := c.stack.Back()
   c.stack.Remove(e)
   return e.Value.(*Memento)
}

type Mario struct {
   score  int64
   status MarioStatus
}

/**
 * @Author: Jason Pang
 * @Description: 展示信息和分数
 * @receiver m
 */
func (m *Mario) ShowInfo() {
   m.status.Name()
   fmt.Println("当前分数为:", m.score)
}

/**
 * @Author: Jason Pang
 * @Description: 创建备忘录
 * @receiver m
 */
func (m *Mario) CreateMemento() *Memento {
   return &Memento{
      mario: &Mario{
         score:  m.score,
         status: m.status,
      },
   }
}

/**
 * @Author: Jason Pang
 * @Description: 恢复数据
 * @receiver m
 * @param mem
 */
func (m *Mario) SetMemento(mem *Memento) {
   m.score = mem.mario.score
   m.status = mem.mario.status
}

type MarioStatus interface {
   Name()
   ObtainMushroom()
   ObtainCape()
   MeetMonster()
   SetMario(mario *Mario)
}

/**
 * @Author: Jason Pang
 * @Description: 小马里奥
 */
type SmallMarioStatus struct {
   mario *Mario
}

/**
 * @Author: Jason Pang
 * @Description: 设置马里奥
 * @receiver s
 * @param mario
 */
func (s *SmallMarioStatus) SetMario(mario *Mario) {
   s.mario = mario
}

func (s *SmallMarioStatus) Name() {
   fmt.Println("小马里奥")
}

/**
 * @Author: Jason Pang
 * @Description: 获得蘑菇变为超级马里奥
 * @receiver s
 */
func (s *SmallMarioStatus) ObtainMushroom() {
   s.mario.status = &SuperMarioStatus{
      mario: s.mario,
   }
   s.mario.score += 100
}

/**
 * @Author: Jason Pang
 * @Description: 获得斗篷变为斗篷马里奥
 * @receiver s
 */
func (s *SmallMarioStatus) ObtainCape() {
   s.mario.status = &CapeMarioStatus{
      mario: s.mario,
   }
   s.mario.score += 200
}

/**
 * @Author: Jason Pang
 * @Description: 遇到怪兽减100
 * @receiver s
 */
func (s *SmallMarioStatus) MeetMonster() {
   s.mario.score -= 100
}

/**
 * @Author: Jason Pang
 * @Description: 超级马里奥
 */

type SuperMarioStatus struct {
   mario *Mario
}

/**
 * @Author: Jason Pang
 * @Description: 设置马里奥
 * @receiver s
 * @param mario
 */
func (s *SuperMarioStatus) SetMario(mario *Mario) {
   s.mario = mario
}

func (s *SuperMarioStatus) Name() {
   fmt.Println("超级马里奥")
}

/**
 * @Author: Jason Pang
 * @Description: 获得蘑菇无变化
 * @receiver s
 */
func (s *SuperMarioStatus) ObtainMushroom() {

}

/**
 * @Author: Jason Pang
 * @Description:获得斗篷变为斗篷马里奥
 * @receiver s
 */
func (s *SuperMarioStatus) ObtainCape() {
   s.mario.status = &CapeMarioStatus{
      mario: s.mario,
   }
   s.mario.score += 200
}

/**
 * @Author: Jason Pang
 * @Description: 遇到怪兽变为小马里奥
 * @receiver s
 */
func (s *SuperMarioStatus) MeetMonster() {
   s.mario.status = &SmallMarioStatus{
      mario: s.mario,
   }
   s.mario.score -= 200
}

/**
 * @Author: Jason Pang
 * @Description: 斗篷马里奥
 */
type CapeMarioStatus struct {
   mario *Mario
}

/**
 * @Author: Jason Pang
 * @Description: 设置马里奥
 * @receiver s
 * @param mario
 */
func (c *CapeMarioStatus) SetMario(mario *Mario) {
   c.mario = mario
}

func (c *CapeMarioStatus) Name() {
   fmt.Println("斗篷马里奥")
}

/**
 * @Author: Jason Pang
 * @Description:获得蘑菇无变化
 * @receiver c
 */
func (c *CapeMarioStatus) ObtainMushroom() {

}

/**
 * @Author: Jason Pang
 * @Description: 获得斗篷无变化
 * @receiver c
 */
func (c *CapeMarioStatus) ObtainCape() {

}

/**
 * @Author: Jason Pang
 * @Description: 遇到怪兽变为小马里奥
 * @receiver c
 */
func (c *CapeMarioStatus) MeetMonster() {
   c.mario.status = &SmallMarioStatus{
      mario: c.mario,
   }
   c.mario.score -= 200
}
func main() {
   caretaker := &Caretaker{
      stack: list.New(),
   }

   mario := Mario{
      status: &SmallMarioStatus{},
      score:  0,
   }
   mario.status.SetMario(&mario)

   mario.status.Name()
   fmt.Println("-------------------获得蘑菇\n")
   mario.status.ObtainMushroom()

   mario.status.Name()
   fmt.Println("-------------------获得斗篷\n")
   mario.status.ObtainCape()

   fmt.Println("-------------------备份一下,要打怪了,当前状态为\n")
   mario.ShowInfo()
   caretaker.Save(mario.CreateMemento())
   fmt.Println("-------------------开始打怪\n")

   mario.status.Name()
   fmt.Println("-------------------遇到怪兽\n")
   mario.status.MeetMonster()

   fmt.Println("-------------------打怪失败,目前状态为\n")
   mario.ShowInfo()

   fmt.Println("-------------------恢复状态,重新打怪\n")
   mario.SetMemento(caretaker.Pop())
   mario.ShowInfo()
}

输出:

➜ myproject go run main.go

小马里奥

-------------------获得蘑菇

超级马里奥

-------------------获得斗篷

-------------------备份一下,要打怪了,当前状态为

斗篷马里奥

当前分数为: 300

-------------------开始打怪

斗篷马里奥

-------------------遇到怪兽

-------------------打怪失败,目前状态为

小马里奥

当前分数为: 100

-------------------恢复状态,重新打怪

斗篷马里奥

当前分数为: 300

总结

简单写了一个小功能,还是挺麻烦的,我想这也是大家不太想用设计模式的一个原因。但是当使用的时候,却发现这个设计模式能够方便的实现很多功能,这也是有人想用设计模式的原因。

备忘录模式虽然不常用,但是对合适的场景还是很有帮助的。

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

我的个人博客为:https://shidawuhen.github.io/

往期文章回顾:

  1. 设计模式
  2. 招聘
  3. 思考
  4. 存储
  5. 算法系列
  6. 读书笔记
  7. 小工具
  8. 架构
  9. 网络
  10. Go语言
相关文章
|
2月前
|
存储 设计模式 安全
Java设计模式-备忘录模式(23)
Java设计模式-备忘录模式(23)
|
3月前
|
设计模式 Go
go 设计模式之观察者模式
go 设计模式之观察者模式
|
3月前
|
设计模式 算法
设计模式的基础问题之备忘录模式在软件开发的问题如何解决
设计模式的基础问题之备忘录模式在软件开发的问题如何解决
|
4月前
|
设计模式 Go
Go语言设计模式:使用Option模式简化类的初始化
在Go语言中,面对构造函数参数过多导致的复杂性问题,可以采用Option模式。Option模式通过函数选项提供灵活的配置,增强了构造函数的可读性和可扩展性。以`Foo`为例,通过定义如`WithName`、`WithAge`、`WithDB`等设置器函数,调用者可以选择性地传递所需参数,避免了记忆参数顺序和类型。这种模式提升了代码的维护性和灵活性,特别是在处理多配置场景时。
72 8
|
3月前
|
设计模式 存储 Go
|
5月前
|
设计模式
备忘录模式-大话设计模式
备忘录模式-大话设计模式
|
4月前
|
设计模式 数据处理
iLogtail设计模式问题之什么是备忘录模式
iLogtail设计模式问题之什么是备忘录模式
|
5月前
|
设计模式 存储 Java
Java设计模式之备忘录模式详解
Java设计模式之备忘录模式详解
|
6月前
|
设计模式 Go
[设计模式 Go实现] 结构型~享元模式
[设计模式 Go实现] 结构型~享元模式
|
6月前
|
设计模式 Go API
[设计模式 Go实现] 结构型~外观模式
[设计模式 Go实现] 结构型~外观模式