一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

實(shí)踐GoF的23種設(shè)計(jì)模式:備忘錄模式

元閏子的邀請(qǐng) ? 來源:元閏子的邀請(qǐng) ? 2023-11-25 09:05 ? 次閱讀

簡(jiǎn)介

相對(duì)于代理模式、工廠模式等設(shè)計(jì)模式,備忘錄模式(Memento)在我們?nèi)粘i_發(fā)中出鏡率并不高,除了應(yīng)用場(chǎng)景的限制之外,另一個(gè)原因,可能是備忘錄模式 UML 結(jié)構(gòu)的幾個(gè)概念比較晦澀難懂,難以映射到代碼實(shí)現(xiàn)中。比如 Originator(原發(fā)器)和 Caretaker(負(fù)責(zé)人),從字面上很難看出它們?cè)谀J街械穆氊?zé)。

但從定義來看,備忘錄模式又是簡(jiǎn)單易懂的,GoF 對(duì)備忘錄模式的定義如下:

Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.

也即,在不破壞封裝的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外進(jìn)行保存,以便在未來將對(duì)象恢復(fù)到原先保存的狀態(tài)。

從定義上看,備忘錄模式有幾個(gè)關(guān)鍵點(diǎn):封裝、保存、恢復(fù)。

對(duì)狀態(tài)的封裝,主要是為了未來狀態(tài)修改或擴(kuò)展時(shí),不會(huì)引發(fā)霰彈式修改;保存和恢復(fù)則是備忘錄模式的主要特點(diǎn),能夠?qū)Ξ?dāng)前對(duì)象的狀態(tài)進(jìn)行保存,并能夠在未來某一時(shí)刻恢復(fù)出來。

現(xiàn)在,在回過頭來看備忘錄模式的 3 個(gè)角色就比較好理解了:

Memento(備忘錄):是對(duì)狀態(tài)的封裝,可以是struct,也可以是interface。

Originator(原發(fā)器):備忘錄的創(chuàng)建者,備忘錄里存儲(chǔ)的就是 Originator 的狀態(tài)。

Caretaker(負(fù)責(zé)人):負(fù)責(zé)對(duì)備忘錄的保存和恢復(fù),無須知道備忘錄中的實(shí)現(xiàn)細(xì)節(jié)。

UML 結(jié)構(gòu)

1d4190e0-8b2d-11ee-939d-92fbcf53809c.png

場(chǎng)景上下文

在前文【Go實(shí)現(xiàn)】實(shí)踐GoF的23種設(shè)計(jì)模式:命令模式我們提到,在簡(jiǎn)單的分布式應(yīng)用系統(tǒng)(示例代碼工程)中,db 模塊用來存儲(chǔ)服務(wù)注冊(cè)信息和系統(tǒng)監(jiān)控?cái)?shù)據(jù)。其中,服務(wù)注冊(cè)信息拆成了profiles和regions兩個(gè)表,在服務(wù)發(fā)現(xiàn)的業(yè)務(wù)邏輯中,通常需要同時(shí)操作兩個(gè)表,為了避免兩個(gè)表數(shù)據(jù)不一致的問題,db 模塊需要提供事務(wù)功能:

1d75d79c-8b2d-11ee-939d-92fbcf53809c.png

事務(wù)的核心功能之一是,當(dāng)其中某個(gè)語句執(zhí)行失敗時(shí),之前已執(zhí)行成功的語句能夠回滾,前文我們已經(jīng)介紹如何基于命令模式搭建事務(wù)框架,下面我們將重點(diǎn)介紹,如何基于備忘錄模式實(shí)現(xiàn)失敗回滾的功能。

代碼實(shí)現(xiàn)

//demo/db/transaction.go
packagedb

//Command執(zhí)行數(shù)據(jù)庫(kù)操作的命令接口,同時(shí)也是備忘錄接口
//關(guān)鍵點(diǎn)1:定義Memento接口,其中Exec方法相當(dāng)于UML圖中的SetState方法,調(diào)用后會(huì)將狀態(tài)保存至Db中
typeCommandinterface{
Exec()error//Exec執(zhí)行insert、update、delete命令
Undo()//Undo回滾命令
setDb(dbDb)//SetDb設(shè)置關(guān)聯(lián)的數(shù)據(jù)庫(kù)
}

//關(guān)鍵點(diǎn)2:定義Originator,在本例子中,狀態(tài)都是存儲(chǔ)在Db對(duì)象中
typeDbinterface{...}

//TransactionDb事務(wù)實(shí)現(xiàn),事務(wù)接口的調(diào)用順序?yàn)閎egin->exec->exec>...->commit
//關(guān)鍵點(diǎn)3:定義Caretaker,Transaction里實(shí)現(xiàn)了對(duì)語句的執(zhí)行(Do)和回滾(Undo)操作
typeTransactionstruct{
namestring
//關(guān)鍵點(diǎn)4:在Caretaker(Transaction)中引用Originator(Db)對(duì)象,用于后續(xù)對(duì)其狀態(tài)的保存和恢復(fù)
dbDb
//注意,這里的cmds并非備忘錄列表,真正的history在Commit方法中
cmds[]Command
}
//Begin開啟一個(gè)事務(wù)
func(t*Transaction)Begin(){
t.cmds=make([]Command,0)
}
//Exec在事務(wù)中執(zhí)行命令,先緩存到cmds隊(duì)列中,等commit時(shí)再執(zhí)行
func(t*Transaction)Exec(cmdCommand)error{
ift.cmds==nil{
returnErrTransactionNotBegin
}
cmd.setDb(t.db)
t.cmds=append(t.cmds,cmd)
returnnil
}
//Commit提交事務(wù),執(zhí)行隊(duì)列中的命令,如果有命令失敗,則回滾后返回錯(cuò)誤
func(t*Transaction)Commit()error{
//關(guān)鍵點(diǎn)5:定義備忘錄列表,用于保存某一時(shí)刻的系統(tǒng)狀態(tài)
history:=&cmdHistory{history:make([]Command,0,len(t.cmds))}
for_,cmd:=ranget.cmds{
//關(guān)鍵點(diǎn)6:執(zhí)行Do方法
iferr:=cmd.Exec();err!=nil{
//關(guān)鍵點(diǎn)8:當(dāng)Do方法執(zhí)行失敗時(shí),則進(jìn)行Undo操作,根據(jù)備忘錄history中的狀態(tài)進(jìn)行回滾
history.rollback()
returnerr
}
//關(guān)鍵點(diǎn)7:如果Do方法執(zhí)行成功,則將狀態(tài)(cmd)保存在備忘錄history中
history.add(cmd)
}
returnnil
}
//cmdHistory命令執(zhí)行歷史
typecmdHistorystruct{
history[]Command
}
func(c*cmdHistory)add(cmdCommand){
c.history=append(c.history,cmd)
}

func(c*cmdHistory)rollback(){
fori:=len(c.history)-1;i>=0;i--{
c.history[i].Undo()
}
}

//InsertCmd插入命令
//關(guān)鍵點(diǎn)9:定義具體的備忘錄類,實(shí)現(xiàn)Memento接口
typeInsertCmdstruct{
dbDb
tableNamestring
primaryKeyinterface{}
newRecordinterface{}
}

func(i*InsertCmd)Exec()error{
returni.db.Insert(i.tableName,i.primaryKey,i.newRecord)
}
func(i*InsertCmd)Undo(){
i.db.Delete(i.tableName,i.primaryKey)
}
func(i*InsertCmd)setDb(dbDb){
i.db=db
}

//UpdateCmd更新命令
typeUpdateCmdstruct{...}
//DeleteCmd刪除命令
typeDeleteCmdstruct{...}

客戶端可以這么使用:

funcclient(){
transaction:=db.CreateTransaction("register"+profile.Id)
transaction.Begin()
rcmd:=db.NewUpdateCmd(regionTable).WithPrimaryKey(profile.Region.Id).WithRecord(profile.Region)
transaction.Exec(rcmd)
pcmd:=db.NewUpdateCmd(profileTable).WithPrimaryKey(profile.Id).WithRecord(profile.ToTableRecord())
transaction.Exec(pcmd)
iferr:=transaction.Commit();err!=nil{
return...
}
return...
}

這里并沒有完全按照標(biāo)準(zhǔn)的備忘錄模式 UML 進(jìn)行實(shí)現(xiàn),但本質(zhì)是一樣的,總結(jié)起來有以下幾個(gè)關(guān)鍵點(diǎn):

定義抽象備忘錄 Memento 接口,這里為Command接口。Command的實(shí)現(xiàn)是具體的數(shù)據(jù)庫(kù)執(zhí)行操作,并且存有對(duì)應(yīng)的回滾操作,比如InsertCmd為“插入”操作,其對(duì)應(yīng)的回滾操作為“刪除”,我們保存的狀態(tài)就是“刪除”這一回滾操作。

定義 Originator 結(jié)構(gòu)體/接口,這里為Db接口。備忘錄Command記錄的就是它的狀態(tài)。

定義 Caretaker 結(jié)構(gòu)體/接口,這里為Transaction結(jié)構(gòu)體。Transaction采用了延遲執(zhí)行的設(shè)計(jì),當(dāng)調(diào)用Exec方法時(shí)只會(huì)將命令緩存到cmds隊(duì)列中,等到調(diào)用Commit方法時(shí)才會(huì)執(zhí)行。

在 Caretaker 中引用 Originator 對(duì)象,用于后續(xù)對(duì)其狀態(tài)的保存和恢復(fù)。這里為Transaction聚合了Db。

在 Caretaker 中定義備忘錄列表,用于保存某一時(shí)刻的系統(tǒng)狀態(tài)。這里為在Transaction.Commit方法中定義了cmdHistory對(duì)象,保存一直執(zhí)行成功的Command。

執(zhí)行 Caretaker 具體的業(yè)務(wù)邏輯,這里為在Transaction.Commit中調(diào)用Command.Exec方法,執(zhí)行具體的數(shù)據(jù)庫(kù)操作命令。

業(yè)務(wù)邏輯執(zhí)行成功后,保存當(dāng)前的狀態(tài)。這里為調(diào)用cmdHistory.add方法將Command保存起來。

如果業(yè)務(wù)邏輯執(zhí)行失敗,則恢復(fù)到原來的狀態(tài)。這里為調(diào)用cmdHistory.rollback方法,反向執(zhí)行已執(zhí)行成功的Command的Undo方法進(jìn)行狀態(tài)恢復(fù)。

根據(jù)具體的業(yè)務(wù)需要,定義具體的備忘錄,這里定義了InsertCmd、UpdateCmd和DeleteCmd。

擴(kuò)展

MySQL 的 undo log 機(jī)制

MySQL 的undo log(回滾日志)機(jī)制本質(zhì)上用的就是備忘錄模式的思想,前文中Transaction回滾機(jī)制實(shí)現(xiàn)的方法參考的就是 undo log 機(jī)制。

undo log 原理是,在提交事務(wù)之前,會(huì)把該事務(wù)對(duì)應(yīng)的回滾操作(狀態(tài))先保存到 undo log 中,然后再提交事務(wù),當(dāng)出錯(cuò)的時(shí)候 MySQL 就可以利用 undo log 來回滾事務(wù),即恢復(fù)原先的記錄值。

比如,執(zhí)行一條插入語句:

insertintoregion(id,name)values(1,"beijing");

那么,寫入到 undo log 中對(duì)應(yīng)的回滾語句為:

deletefromregionwhereid=1;

當(dāng)執(zhí)行一條語句失敗,需要回滾時(shí),MySQL 就會(huì)從讀取對(duì)應(yīng)的回滾語句來執(zhí)行,從而將數(shù)據(jù)恢復(fù)至事務(wù)提交之前的狀態(tài)。undo log 是 MySQL 實(shí)現(xiàn)事務(wù)回滾和多版本控制(MVCC)的根基。

典型應(yīng)用場(chǎng)景

事務(wù)回滾。事務(wù)回滾的一種常見實(shí)現(xiàn)方法是 undo log,其本質(zhì)上用的就是備忘錄模式。

系統(tǒng)快照(Snapshot)。多版本控制的用法,保存某一時(shí)刻的系統(tǒng)狀態(tài)快照,以便在將來能夠恢復(fù)。

撤銷功能。比如 Microsoft Offices 這類的文檔編輯軟件的撤銷功能。

優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

提供了一種狀態(tài)恢復(fù)的機(jī)制,讓系統(tǒng)能夠方便地回到某個(gè)特定狀態(tài)下。

實(shí)現(xiàn)了對(duì)狀態(tài)的封裝,能夠在不破壞封裝的前提下實(shí)現(xiàn)狀態(tài)的保存和恢復(fù)。

缺點(diǎn)

資源消耗大。系統(tǒng)狀態(tài)的保存意味著存儲(chǔ)空間的消耗,本質(zhì)上是空間換時(shí)間的策略。undo log 是一種折中方案,保存的狀態(tài)并非某一時(shí)刻數(shù)據(jù)庫(kù)的所有數(shù)據(jù),而是一條反操作的 SQL 語句,存儲(chǔ)空間大大減少。

并發(fā)安全。在多線程場(chǎng)景,實(shí)現(xiàn)備忘錄模式時(shí),要注意在保證狀態(tài)的不變性,否則可能會(huì)有并發(fā)安全問題。

與其他模式的關(guān)聯(lián)

在實(shí)現(xiàn) Undo/Redo 操作時(shí),你通常需要同時(shí)使用備忘錄模式與命令模式。

另外,當(dāng)你需要遍歷備忘錄對(duì)象中的成員時(shí),通常會(huì)使用迭代器模式,以防破壞對(duì)象的封裝。

文章配圖

可以在用Keynote畫出手繪風(fēng)格的配圖中找到文章的繪圖方法。






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • UML
    UML
    +關(guān)注

    關(guān)注

    0

    文章

    122

    瀏覽量

    31064
  • MySQL
    +關(guān)注

    關(guān)注

    1

    文章

    840

    瀏覽量

    27344
  • MVCC
    +關(guān)注

    關(guān)注

    0

    文章

    13

    瀏覽量

    1529

原文標(biāo)題:【Go實(shí)現(xiàn)】實(shí)踐GoF的23種設(shè)計(jì)模式:備忘錄模式

文章出處:【微信號(hào):yuanrunzi,微信公眾號(hào):元閏子的邀請(qǐng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    HarmonyOS開發(fā)實(shí)例:【手機(jī)備忘錄

    基于用戶首選項(xiàng),實(shí)現(xiàn)了備忘錄新增、更新、刪除以及查找等功能。
    的頭像 發(fā)表于 04-18 21:40 ?1084次閱讀
    HarmonyOS開發(fā)實(shí)例:【手機(jī)<b class='flag-5'>備忘錄</b>】

    PostgreSQL操作備忘錄

    PostgreSQL 操作備忘錄
    發(fā)表于 05-23 08:48

    UDS診斷命令備忘錄

    UDS實(shí)踐性強(qiáng),邏輯復(fù)雜,很多服務(wù)非要體驗(yàn)過一次才能理解,導(dǎo)致包括我在內(nèi)的初學(xué)者感覺晦澀難懂,不明覺厲,因此將自己的理解寫下來、整理下來,與君共勉。零、UDS診斷命令備忘錄一、簡(jiǎn)介UDS
    發(fā)表于 08-26 16:09

    怎樣去搭建一基于XR806的開源桌面備忘錄

    本人計(jì)劃懟一個(gè)開源桌面備忘錄/天氣預(yù)報(bào)/相冊(cè)的項(xiàng)目基于XR806,同時(shí)學(xué)習(xí)鴻蒙操作系統(tǒng)獲得暈哥贈(zèng)送的開發(fā)板和芯片,目前處于環(huán)境搭建階段看起來這個(gè)芯片玩的人比較少,目前遇到了問題,不知道如何解決,希望
    發(fā)表于 12-28 06:52

    23基本的設(shè)計(jì)模式總結(jié)

    一樣。?提到設(shè)計(jì)模式,不得不感謝GoF(***,四人組),他們1995年出版的《設(shè)計(jì)模式》一書,第一次將設(shè)計(jì)模式提升到理論高度,并將之規(guī)范化。書中一共總結(jié)了
    發(fā)表于 03-01 06:08

    全球半導(dǎo)體聯(lián)盟與中國(guó)半導(dǎo)體行業(yè)簽署合作備忘錄

    全球半導(dǎo)體聯(lián)盟與中國(guó)半導(dǎo)體行業(yè)簽署合作備忘錄 全球半導(dǎo)體聯(lián)盟(GSA)與中國(guó)半導(dǎo)體行業(yè)協(xié)會(huì)(CSIA)在蘇州聯(lián)合申明簽署合作備忘錄。此次合作將為促
    發(fā)表于 09-24 08:17 ?741次閱讀

    戴姆勒與百度簽署諒解備忘錄

    7月25日,奔馳母公司戴姆勒與百度簽署諒解備忘錄,深化雙方在自動(dòng)駕駛和車聯(lián)網(wǎng)等領(lǐng)域的戰(zhàn)略合作。
    的頭像 發(fā)表于 07-28 09:53 ?2847次閱讀

    實(shí)踐GoF23設(shè)計(jì)模式:命令模式簡(jiǎn)介

    因此,我們需要對(duì)請(qǐng)求進(jìn)行抽象,將上下文信息封裝到請(qǐng)求對(duì)象里,這其實(shí)就是命令模式,而該請(qǐng)求對(duì)象就是 Command。
    的頭像 發(fā)表于 01-13 16:36 ?1248次閱讀

    設(shè)計(jì)模式備忘錄設(shè)計(jì)模式

    備忘錄設(shè)計(jì)模式(Memento Design Pattern)是一行為型設(shè)計(jì)模式,它的主要目的是在不破壞對(duì)象封裝性的前提下,捕捉和保存一個(gè)對(duì)象的內(nèi)部狀態(tài)
    的頭像 發(fā)表于 06-06 11:19 ?907次閱讀

    設(shè)計(jì)模式行為型:備忘錄模式

    備忘錄模式(Memento Pattern)保存一個(gè)對(duì)象的某個(gè)狀態(tài),以便在適當(dāng)?shù)臅r(shí)候恢復(fù)對(duì)象。備忘錄模式屬于行為型模式。
    的頭像 發(fā)表于 06-07 11:16 ?972次閱讀
    設(shè)計(jì)<b class='flag-5'>模式</b>行為型:<b class='flag-5'>備忘錄</b><b class='flag-5'>模式</b>

    新思科技同越南政府簽署諒解備忘錄

    在越南總理范明政訪美期間,新思科技與越南國(guó)家創(chuàng)新中心(nic)簽署了關(guān)于培養(yǎng)越南集成電路設(shè)計(jì)人才的諒解備忘錄,支持nic成立芯片設(shè)計(jì)孵化中心。另外,新思科技與越南信息通訊部下屬的信息通信技術(shù)產(chǎn)業(yè)公司簽訂了支援越南半導(dǎo)體產(chǎn)業(yè)發(fā)展的諒解備忘錄。
    的頭像 發(fā)表于 09-20 10:56 ?1697次閱讀

    實(shí)踐GoF23設(shè)計(jì)模式:解釋器模式

    解釋器模式(Interpreter Pattern)應(yīng)該是 GoF23 設(shè)計(jì)模式中使用頻率最少的一
    的頭像 發(fā)表于 04-01 11:01 ?877次閱讀
    <b class='flag-5'>實(shí)踐</b><b class='flag-5'>GoF</b>的<b class='flag-5'>23</b><b class='flag-5'>種</b>設(shè)計(jì)<b class='flag-5'>模式</b>:解釋器<b class='flag-5'>模式</b>

    蘋果iOS 18將支持語音備忘錄及數(shù)學(xué)符號(hào)顯示

    首先是語音備忘錄功能。據(jù)悉,蘋果有意在iOS 18系統(tǒng)中加入此項(xiàng)功能,使iPhone用戶能夠便捷地錄制音頻文件,并將其直接嵌入至備忘錄之中。
    的頭像 發(fā)表于 04-18 11:14 ?748次閱讀

    黑芝麻智能與Dirac簽署合作備忘錄

    1月21日,黑芝麻智能與Dirac簽署合作備忘錄,雙方將基于黑芝麻智能武當(dāng)C1200家族芯片共同探索汽車高品質(zhì)音頻體驗(yàn)的創(chuàng)新。
    的頭像 發(fā)表于 01-21 10:48 ?409次閱讀

    華為與巴塞羅那市政府簽署諒解備忘錄

    MWC25巴塞羅那期間,華為與巴塞羅那市政府簽署智慧城市戰(zhàn)略合作諒解備忘錄(以下簡(jiǎn)稱“本協(xié)議”)。
    的頭像 發(fā)表于 03-07 15:53 ?300次閱讀