欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

学习备忘录模式:使用go编写行为型设计模式中的手撸实例

最编程 2024-08-15 14:32:02
...

手撸golang 行为型设计模式 备忘录模式

缘起

最近复习设计模式 拜读谭勇德的<<设计模式就该这样学>> 本系列笔记拟采用golang练习之

备忘录模式
备忘录模式(Memento Pattern)又叫作快照模式(Snapshot Pattern),
或令牌模式(Token Pattern),
指在不破坏封装的前提下,
捕获一个对象的内部状态,
并在对象之外保存这个状态。
这样以后就可将该对象恢复到原先保存的状态,
属于行为型设计模式。

备忘录模式主要适用于以下应用场景。
(1)需要保存历史快照的场景。
(2)希望在对象之外保存状态,且除了自己,其他类对象无法访问状态保存的具体内容。

(摘自 谭勇德 <<设计模式就该这样学>>)

场景
  • 某线上博客平台, 需为用户提供在线编辑文章功能
  • 文章主要包括标题 - title 和内容 - content等信息
  • 为最大程度防止异常情况导致编辑内容的丢失, 需要提供版本暂存和Undo, Redo功能
  • "版本暂存"问题可以应用备忘录模式, 将编辑器的状态完整保存(主要就是编辑内容)
  • Undo和Redo的本质, 是在历史版本中前后移动

设计
  • IEditor: 定义编辑器接口
  • tEditorMemento: 定义编辑器的备忘录, 也就是编辑器的内部状态数据模型, 同时也对应一个历史版本
  • tMockEditor: 虚拟的编辑器类, 实现IEditor接口

单元测试

memento_pattern_test.go

package behavioral_patterns

import (
	"learning/gooop/behavioral_patterns/memento"
	"testing"
)

func Test_MementoPattern(t *testing.T) {
	editor := memento.NewMockEditor()

	// test save()
	editor.Title("唐诗")
	editor.Content("白日依山尽")
	editor.Save()

	editor.Title("唐诗 登鹳雀楼")
	editor.Content("白日依山尽, 黄河入海流. ")
	editor.Save()

	editor.Title("唐诗 登鹳雀楼 王之涣")
	editor.Content("白日依山尽, 黄河入海流。欲穷千里目, 更上一层楼。")
	editor.Save()

	// test show()
	editor.Show()

	// test undo()
	for {
		e := editor.Undo()
		if e != nil {
			break
		} else {
			editor.Show()
		}
	}

	// test redo()
	for {
		e := editor.Redo()
		if e != nil {
			break
		} else {
			editor.Show()
		}
	}
}

测试输出
$ go test -v memento_pattern_test.go 
=== RUN   Test_MementoPattern
tMockEditor.Show, title=唐诗 登鹳雀楼 王之涣, content=白日依山尽, 黄河入海流。欲穷千里目, 更上一层楼。
tMockEditor.Show, title=唐诗 登鹳雀楼, content=白日依山尽, 黄河入海流. 
tMockEditor.Show, title=唐诗, content=白日依山尽
tMockEditor.Show, title=唐诗 登鹳雀楼, content=白日依山尽, 黄河入海流. 
tMockEditor.Show, title=唐诗 登鹳雀楼 王之涣, content=白日依山尽, 黄河入海流。欲穷千里目, 更上一层楼。
--- PASS: Test_MementoPattern (0.00s)
PASS
ok      command-line-arguments  0.002s

IEditor.go

定义编辑器接口

package memento

type IEditor interface {
	Title(title string)
	Content(content string)
	Save()
	Undo() error
	Redo() error

	Show()
}

tEditorMemento.go

定义编辑器的备忘录, 也就是编辑器的内部状态数据模型, 同时也对应一个历史版本

package memento

import "time"

type tEditorMemento struct {
	title string
	content string
	createTime int64
}

func newEditorMememto(title string, content string) *tEditorMemento {
	return &tEditorMemento{
		title, content, time.Now().Unix(),
	}
}

tMockEditor.go

虚拟的编辑器类, 实现IEditor接口

package memento

import (
	"errors"
	"fmt"
)

type tMockEditor struct {
	title string
	content string
	versions []*tEditorMemento
	index int
}

func NewMockEditor() IEditor {
	return &tMockEditor{
		"", "", make([]*tEditorMemento, 0), 0,
	}
}

func (me *tMockEditor) Title(title string) {
	me.title = title
}

func (me *tMockEditor) Content(content string) {
	me.content = content
}

func (me *tMockEditor) Save() {
	it := newEditorMememto(me.title, me.content)
	me.versions = append(me.versions, it)
	me.index = len(me.versions) - 1
}

func (me *tMockEditor) Undo() error {
	return me.load(me.index - 1)
}

func (me *tMockEditor) load(i int) error {
	size := len(me.versions)
	if size <= 0 {
		return errors.New("no history versions")
	}

	if i < 0 || i >= size {
		return errors.New("no more history versions")
	}

	it := me.versions[i]
	me.title = it.title
	me.content = it.content
	me.index = i
	return nil
}

func (me *tMockEditor) Redo() error {
	return me.load(me.index + 1)
}

func (me *tMockEditor) Show() {
	fmt.Printf("tMockEditor.Show, title=%s, content=%s\n", me.title, me.content)
}

备忘录模式小结
备忘录模式的优点
(1)简化发起人实体类(Originator)的职责,隔离状态存储与获取,
    实现了信息的封装,客户端无须关心状态的保存细节。
(2)提供状态回滚功能。

备忘录模式的缺点
备忘录模式的缺点主要是消耗资源。
如果需要保存的状态过多,则每一次保存都会消耗很多内存。

(摘自 谭勇德 <<设计模式就该这样学>>)

(end)

推荐阅读