當初學備忘錄模式的時候,特別開心,這不就是游戲里的備份嘛!游戲關閉之后,重新開啟,從上次結束的位置繼續開始,但終歸沒有進入游戲行業,也沒有機會用過備忘錄模式,
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分析
從定義上看,除了不破壞封裝性外,其它都比較容易理解,對于不破壞封裝性,我覺得有兩點:
-
備忘錄是Originator自己創建的
-
備忘錄應該只有獲取相關的介面,不應該有修改相關的介面
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/

往期文章回顧:
-
設計模式
-
招聘
-
思考
-
存盤
-
演算法系列
-
讀書筆記
-
小工具
-
架構
-
網路
-
Go語言
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/294561.html
標籤:其他
上一篇:關于運行ET框架Demo時遇到的問題(Error Code 10061)
下一篇:牛客第十場 題解
