本文源碼: GitHub·點這裡 || GitEE·點這裡
一、生活場景
1、場景描述
常見的視訊播放軟體都具備這樣一個功能:假設在播放視訊西遊記,如果這時候切換播放視訊紅樓夢,當再次切回播放西遊記時,視訊會從上次切走的時間點繼續播放。下面基于備忘錄設計模式來描述該場景流程。
2、場景圖解

3、代碼實作
public class C01_InScene {
public static void main(String[] args) {
Record record = new Record() ;
Player player = new Player() ;
PlayData pd1 = new PlayData("西遊記","19:19") ;
PlayData pd2 = new PlayData("紅樓夢","29:19") ;
player.setPlayData(pd1);
player.saveProgress() ;
System.out.println("正在播放:"+
player.getPlayData().getVideoName()+":"+
player.getPlayData().getPlayTime());
record.put(new Progress(pd1));
System.out.println("===切換播放視訊===");
player.setPlayData(pd2);
player.saveProgress() ;
System.out.println("正在播放:"+
player.getPlayData().getVideoName()+":"+
player.getPlayData().getPlayTime());
record.put(new Progress(pd1));
System.out.println("===切回上個視訊===");
player.resumeProgress(record.get(pd1.getVideoName()));
System.out.println("正在播放:"+
player.getPlayData().getVideoName()+":"+
player.getPlayData().getPlayTime());
}
}
/**
* 視訊播放器
*/
class Player {
private PlayData playData ;
public PlayData getPlayData() {
return playData;
}
public void setPlayData(PlayData playData) {
this.playData = playData;
}
public Progress saveProgress (){
return new Progress(playData) ;
}
public void resumeProgress (Progress progress){
playData = progress.getPlayData() ;
}
}
/**
* 播放進度
*/
class Progress {
private PlayData playData ;
public Progress (PlayData playData){
this.playData = playData ;
}
public PlayData getPlayData() {
return playData ;
}
}
/**
* 播放記錄
*/
class Record {
private Map<String,Progress> dataMap = new HashMap<>() ;
public void put (Progress progress){
dataMap.put(progress.getPlayData().getVideoName(),progress) ;
}
public Progress get (String videoName){
return dataMap.get(videoName) ;
}
}
/**
* 播放狀态描述
*/
class PlayData {
private String videoName ;
private String playTime ;
public PlayData(String videoName, String playTime) {
this.videoName = videoName;
this.playTime = playTime;
}
public String getVideoName() {
return videoName;
}
public void setVideoName(String videoName) {
this.videoName = videoName;
}
public String getPlayTime() {
return playTime;
}
public void setPlayTime(String playTime) {
this.playTime = playTime;
}
}
執行效果:
正在播放:西遊記:19:19
===切換播放視訊===
正在播放:紅樓夢:29:19
===切回上個視訊===
正在播放:西遊記:19:19
二、備忘錄模式
1、基礎概念
備忘錄模式屬于行為型模式,其用意在不破壞封裝性的前提下,捕獲一個對象的内部狀态,并在該對象之外儲存這個狀态。後續可将該對象恢複到原先儲存的狀态。備忘錄對象主要用來記錄一個對象的某種狀态,或者某些資料,當要做回退時,可以從備忘錄對象裡擷取原來的資料進行恢複操作。
2、模式圖解
3、核心角色
- 備忘錄角色
負責儲存對象狀态的記錄,即Originator内部狀态。
- 發起人角色
建立一個含有目前的内部狀态的備忘錄對象,用來儲存狀态。
- 守護者對象
提供合理的方式,負責儲存多個備忘錄對象。
4、源碼實作
public class C02_Memento {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("狀态1:State01");
caretaker.add(originator.saveStateMemento());
originator.setState("狀态2:State02");
caretaker.add(originator.saveStateMemento());
System.out.println("目前的狀态是 =" + originator.getState());
// 恢複狀态
originator.getStateFromMemento(caretaker.get(0));
System.out.println("目前的狀态是 =" + originator.getState());
}
}
/**
* 守護者對象
*/
class Caretaker {
private List<Memento> mementoList = new ArrayList<>();
public void add(Memento memento) {
mementoList.add(memento);
}
public Memento get (int index) {
return mementoList.get(index);
}
}
/**
* 備忘錄角色
*/
class Memento {
private String state;
public Memento(String state) {
super();
this.state = state;
}
public String getState() {
return state;
}
}
/**
* 發起人角色
*/
class Originator {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Memento saveStateMemento() {
return new Memento(state);
}
public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
三、模式總結
1、優缺點描述
備忘錄模式提供一種可以恢複狀态的機制,實作狀态的封裝,能夠比較友善地回到某個曆史的狀态;常常與指令模式和疊代器模式一同使用。如果類的成員變量過多,會占用比較大的記憶體資源,為了節約記憶體,備忘錄模式可以和原型模式配合使用。
2、應用場景
- 浏覽器的前進和回退;
- 資料庫備份與還原;
- 編輯器
撤銷;Ctrl+Z
- 虛拟機生成快照與恢複;
- Git版本管理,代碼的送出和復原。
四、源代碼位址
GitHub·位址
https://github.com/cicadasmile/model-arithmetic-parent
GitEE·位址
https://gitee.com/cicadasmile/model-arithmetic-parent