美文网首页程序员设计模式
手撸golang 行为型设计模式 备忘录模式

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

作者: 老罗话编程 | 来源:发表于2021-02-09 17:30 被阅读0次

    手撸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)

    相关文章

      网友评论

        本文标题:手撸golang 行为型设计模式 备忘录模式

        本文链接:https://www.haomeiwen.com/subject/dtjcxltx.html