手擼golang 行爲型設計模式 備忘錄模式

手擼golang 行爲型設計模式 備忘錄模式

緣起

最近複習設計模式
拜讀譚勇德的<<設計模式就該這樣學>>
本系列筆記擬採用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

定義編輯器接口app

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接口oop

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)單元測試

相關文章
相關標籤/搜索