Java設(shè)計(jì)模式之備忘錄模式
無(wú)論是我們?cè)谑褂脀ord還是記事本,系統(tǒng)都會(huì)為我們提供撤銷的功能,這幾乎是人人都會(huì)使用到的功能,而在我們實(shí)際開(kāi)發(fā)中,會(huì)不會(huì)存在一個(gè)很復(fù)雜的對(duì)象,當(dāng)更改了其中的某一個(gè)屬性以后,也提供撤銷的功能,可以快速恢復(fù)到更新前的狀態(tài)。提供該功能的模式也正是今天的主題——備忘錄模式。
一、概念理解
書(shū)上備忘錄的解釋是,在不破壞封裝的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài),這樣可以在以后將對(duì)象恢復(fù)到原先保存的狀態(tài)。
其實(shí)也就是在一個(gè)對(duì)象之外再額外的增加一個(gè)副本對(duì)象,每當(dāng)我們?cè)趯?duì)象上更改一些屬性以后就構(gòu)建一個(gè)副本,并把副本存放在一個(gè)隊(duì)列中,每當(dāng)回退該對(duì)象的時(shí)候就從副本中恢復(fù)數(shù)據(jù)。
很顯然需要三個(gè)角色:原對(duì)象、副本對(duì)象、存放副本的隊(duì)列。
也即書(shū)上的三個(gè)角色定義:
Originator(發(fā)起人角色):負(fù)責(zé)創(chuàng)建一個(gè)備忘錄,記錄自身需要保存的狀態(tài),具備狀態(tài)回滾功能;即原對(duì)象。
Memento(備忘錄角色):用于存儲(chǔ)Originator的內(nèi)部狀態(tài),且可以防止Originator以外的對(duì)象進(jìn)行訪問(wèn);即副本對(duì)象。
Caretaker(管理員角色):負(fù)責(zé)存儲(chǔ)、提供管理Memento,無(wú)法對(duì)Memento的內(nèi)容進(jìn)行操作和訪問(wèn);也即存放副本的隊(duì)列。
為了讓概念落地,我們基于備忘錄模式的思想實(shí)現(xiàn)視頻草稿箱的功能。
在這個(gè)業(yè)務(wù)場(chǎng)景中,發(fā)起人其實(shí)就是編輯視頻的編輯器,在這個(gè)編輯器中我們能干啥呢?編輯視頻、從草稿箱導(dǎo)入、保存到草稿箱。這不是就是發(fā)起人角色的作用嗎!我們稱為編輯草稿。
備忘錄那不就是草稿修改后嗎,我們稱之為完稿。
完稿做好了,要存放到一個(gè)列表中用于我們快速修復(fù),那當(dāng)然就是管理員角色。
基于三個(gè)角色我們實(shí)現(xiàn)代碼。
二、案例實(shí)現(xiàn)
備忘錄角色(完稿):
就是個(gè)基礎(chǔ)對(duì)象,用于存儲(chǔ)數(shù)據(jù),視頻包含的屬性主要是,標(biāo)題、封面、視頻地址
/**
* 備忘錄(完稿)
* @author tcy
* @Date 16-09-2022
*/
public class VideoMemento {
private String title;
private String videoUrl;
private String imgs;
public VideoMemento(String title, String content, String imgs) {
this.title = title;
this.videoUrl = content;
this.imgs = imgs;
}
public String getTitle() {
return title;
}
public String getContent() {
return videoUrl;
}
public String getImgs() {
return imgs;
}
@Override
public String toString() {
return "ArticleMemento{" +
"title='" + title + '\'' +
", content='" + videoUrl + '\'' +
", imgs='" + imgs + '\'' +
'}';
}
}發(fā)起人角色(編輯草稿):
在基本對(duì)象之上,增加保存和撤銷的操作,可以看到和我們定義的完稿長(zhǎng)的一樣,只是又增了saveToMemento()方法和undoFromMemento()方法,用于保存和撤銷。
/**
* 發(fā)起人 (草稿)
* @author tcy
* @Date 16-09-2022
*/
public class Editor {
private String title;
private String videoUrl;
private String imgs;
public Editor(String title, String videoUrl, String imgs) {
this.title = title;
this.videoUrl = videoUrl;
this.imgs = imgs;
}
public String getTitle() {
return title;
}
public String getContent() {
return videoUrl;
}
public String getImgs() {
return imgs;
}
public void setTitle(String title) {
this.title = title;
}
public void setContent(String content) {
this.videoUrl = content;
}
public void setImgs(String imgs) {
this.imgs = imgs;
}
/**
* 保存到備忘錄
* @return
*/
public VideoMemento saveToMemento(){
VideoMemento articleMemento = new VideoMemento(this.title,this.videoUrl,this.imgs);
return articleMemento;
}
/**
* 從備忘錄恢復(fù)
* @param articleMemento
*/
public void undoFromMemento(VideoMemento articleMemento){
this.title = articleMemento.getTitle();
this.videoUrl = articleMemento.getContent();
this.imgs = articleMemento.getImgs();
}
@Override
public String toString() {
return "Editor{" +
"title='" + title + '\'' +
", content='" + videoUrl + '\'' +
", imgs='" + imgs + '\'' +
'}';
}
}管理員角色(草稿箱):
包括一個(gè)棧,用于存儲(chǔ)完稿,利用棧先進(jìn)后出的特性,實(shí)現(xiàn)逐步的撤銷。
/**
* 管理者(草稿箱)
* @author tcy
* @Date 16-09-2022
*/
public class Caretaker {
private final Stack<VideoMemento> STACK = new Stack<VideoMemento>();
public VideoMemento getMemento(){
VideoMemento videoMemento = STACK.pop();
return videoMemento;
}
public void addMemento(VideoMemento videoMemento){
STACK.push(videoMemento);
}
}讀者可以拉取完整代碼到本地進(jìn)行學(xué)習(xí),實(shí)現(xiàn)代碼均測(cè)試通過(guò)后上傳到碼云,本地下載。
三、總結(jié)
備忘錄模式是怎么回事想必你已經(jīng)明白了,如果我們?cè)趯?shí)際開(kāi)發(fā)中有一個(gè)對(duì)象的功能很復(fù)雜,屬性非常的多,這時(shí)候備忘錄模式就是一個(gè)好的選擇。
備忘錄模式實(shí)現(xiàn)也很簡(jiǎn)單,在原對(duì)象基礎(chǔ)之上再增加一個(gè)副本對(duì)象,在原對(duì)象上額外的增加兩個(gè)方法,用于構(gòu)建一個(gè)副本和從副本中取值。再創(chuàng)建一個(gè)棧對(duì)象,用于存儲(chǔ)、管理副本。
備忘錄模式的優(yōu)勢(shì)突出,會(huì)簡(jiǎn)化發(fā)起人(原對(duì)象)的職責(zé),隔離存儲(chǔ)狀態(tài),實(shí)現(xiàn)信息的封裝,客戶端無(wú)須關(guān)心保存細(xì)節(jié),而且提供了狀態(tài)回滾功能。
但是最顯著的缺點(diǎn)就是消耗資源,如果對(duì)象改動(dòng)較大,每一次保存都會(huì)消耗很大的內(nèi)存空間,功能換空間。
備忘錄模式在Jdk和Spring中應(yīng)用的并不多,在我們實(shí)際應(yīng)用中我們要衡量空間和效率的影響,是否使用備忘錄模式進(jìn)行合理的取舍。
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接
相關(guān)文章
mybatisPlus中apply的使用以進(jìn)行聯(lián)表等復(fù)雜sql語(yǔ)句詳解
在MyBatis-Plus中,`apply()`方法可以用于添加任意的SQL片段,包括聯(lián)表查詢,使用`apply()`方法的好處是可以直接添加原生的SQL片段,而不受MyBatis-Plus提供的常規(guī)查詢條件構(gòu)建方法的限制,但是,使用`apply()`方法需要注意安全性和性能問(wèn)題2025-03-03
搭建Spring Boot聚合項(xiàng)目的實(shí)現(xiàn)示例
本文主要介紹了搭建Spring Boot聚合項(xiàng)目的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-04-04
Spring Cloud Alibaba使用Sentinel實(shí)現(xiàn)接口限流
這篇文章主要介紹了Spring Cloud Alibaba使用Sentinel實(shí)現(xiàn)接口限流,本文詳細(xì)的介紹了Sentinel組件的用法以及接口限流,感興趣的可以了解一下2019-04-04
Dubbo無(wú)法訪問(wèn)遠(yuǎn)程Zookeeper已注冊(cè)服務(wù)的問(wèn)題解決方案
今天小編就為大家分享一篇關(guān)于Dubbo無(wú)法訪問(wèn)遠(yuǎn)程Zookeeper已注冊(cè)服務(wù)的問(wèn)題解決方案,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03
SpringMVC處理器映射器HandlerMapping詳解
這篇文章主要介紹了SpringMVC處理器映射器HandlerMapping詳解,在SpringMVC中會(huì)有很多請(qǐng)求,每個(gè)請(qǐng)求都需要一個(gè)HandlerAdapter處理,具體接收到一個(gè)請(qǐng)求之后使用哪個(gè)HandlerAdapter進(jìn)行處理呢,他們的過(guò)程是什么,需要的朋友可以參考下2023-09-09
java實(shí)現(xiàn)分布式項(xiàng)目搭建的方法
這篇文章主要介紹了java實(shí)現(xiàn)分布式項(xiàng)目搭建的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
mybatis 查詢sql中in條件用法詳解(foreach)
這篇文章主要介紹了mybatis 查詢sql中in條件用法詳解(foreach),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
Java數(shù)據(jù)類型實(shí)現(xiàn)自動(dòng)與強(qiáng)制轉(zhuǎn)換的示例代碼
Java數(shù)據(jù)類型之間的轉(zhuǎn)換有自動(dòng)轉(zhuǎn)換和強(qiáng)制類型轉(zhuǎn)換,這篇文章主要給大家介紹Java數(shù)據(jù)類型如何實(shí)現(xiàn)自動(dòng)轉(zhuǎn)換與強(qiáng)制轉(zhuǎn)換,需要的朋友可以參考下2023-05-05

