当系统需要提供「复原功能」、「取消复原功能」、「回复到上一个步骤」等需要将这些资料暂时存放在记忆体内,可以采纳的设计模式。
要思考的是,在确保资料不会「任意被他者」复制、备份,且同时能有顺序地备份资料,供使用者想要「复原」时使用。
作法是:
以下范例以「音乐模拟器」为核心制作。
制作将被储存的资料:Solfege
public class Solfege {
private String name;
public Solfege(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
制作音乐模拟器:MusicSimulator
(Originator 物件)
public class MusicSimulator {
private List<Solfege> inputs = new ArrayList<>();
public void showInputs() {
System.out.println("输入的音阶有:");
for (Solfege solfege : inputs) {
System.out.print(solfege.getName());
}
System.out.println("\n");
}
public void inputSolfege(Solfege solfege) {
inputs.add(solfege);
}
public MusicSimulatorMemento saveInputs() {
System.out.println("开始备份\n");
StringBuilder stringBuilder = new StringBuilder();
for (Solfege input : inputs) {
stringBuilder.append(input.getName());
}
return new MusicSimulatorMemento(Base64.getEncoder().encodeToString(stringBuilder.toString().getBytes()));
}
public void restore(MusicSimulatorMemento musicSimulatorMemento) {
System.out.println("开始还原\n");
if (musicSimulatorMemento == null) {
System.out.println("没有记录");
} else {
String decodedString = new String(Base64.getDecoder().decode(musicSimulatorMemento.getBackup().getBytes()));
inputs.clear();
for (String inputString : decodedString.split("")) {
inputs.add(new Solfege(inputString));
}
}
}
}
制作负责储存资料的物件:MusicSimulatorMemento
(Memento物件)
public class MusicSimulatorMemento {
private String backup;
public MusicSimulatorMemento(String backup) {
this.backup = backup;
}
public String getBackup() {
return backup;
}
}
制作建立负责储存多个 Memento 的物件:MusicSimulatorCareTaker
(Caretaker物件)
public class MusicSimulatorCareTaker {
private List<MusicSimulatorMemento> saves = new ArrayList<>();
public MusicSimulatorMemento getUndo() {
if (saves.isEmpty()) {
return null;
} else {
return saves.get(saves.size() - 1);
}
}
public void setSave(MusicSimulatorMemento memento) {
saves.add(memento);
}
}
测试,在模拟器上输入音符後,还原到上个状态:MusicSimulatorMementoSample
public class MusicSimulatorMementoSample {
public static void main(String[] args) {
MusicSimulator musicSimulator = new MusicSimulator();
// 输入音阶
musicSimulator.inputSolfege(new Solfege("C"));
musicSimulator.inputSolfege(new Solfege("D"));
musicSimulator.inputSolfege(new Solfege("E"));
musicSimulator.inputSolfege(new Solfege("F"));
// 确认输入的音阶
musicSimulator.showInputs();
// 储存
MusicSimulatorCareTaker musicSimulatorCareTaker = new MusicSimulatorCareTaker();
musicSimulatorCareTaker.setSave(musicSimulator.saveInputs());
// 输入新的音阶
musicSimulator.inputSolfege(new Solfege("G"));
musicSimulator.inputSolfege(new Solfege("A"));
musicSimulator.inputSolfege(new Solfege("B"));
// 确认输入的音阶
musicSimulator.showInputs();
// 复原到上个状态
musicSimulator.restore(musicSimulatorCareTaker.getUndo());
// 确认输入的音阶
musicSimulator.showInputs();
}
}
制作将被储存的资料:Solfege
class Solfege {
constructor(name) {
/** @type {string} */
this.name = name;
}
getName() {
return this.name;
}
}
制作音乐模拟器:MusicSimulator
(Originator 物件)
class MusicSimulator {
constructor() {
/** @type {Solfege[]} */
this.inputs = [];
}
showInputs() {
console.log("输入的音阶有:");
let solfegeList = ""
for (const solfege of this.inputs) {
solfegeList += solfege.getName();
}
console.log(`${solfegeList}\n`);
}
/** @param {Solfege} solfege */
inputSolfege(solfege) {
this.inputs.push(solfege);
}
saveInputs() {
console.log("开始备份\n");
let currentInputs = "";
for (const solfege of this.inputs) {
currentInputs += solfege.getName();
}
return new MusicSimulatorMemento(Buffer.from(currentInputs, 'utf8').toString('base64'));
}
/** @param {MusicSimulatorMemento} musicSimulatorMemento */
restore(musicSimulatorMemento) {
console.log("开始还原\n");
if (musicSimulatorMemento === null) {
console.log("没有记录");
} else {
const decodedString = Buffer.from(musicSimulatorMemento.getBackup(), 'base64').toString('utf8');
this.inputs = [];
for (const inputString of decodedString.split("")) {
this.inputs.push(new Solfege(inputString));
}
}
}
}
制作负责储存资料的物件:MusicSimulatorMemento
(Memento物件)
class MusicSimulatorMemento {
constructor(backup) {
/** @type {string} */
this.backup = backup;
}
getBackup() {
return this.backup;
}
}
制作建立负责储存多个 Memento 的物件:MusicSimulatorCareTaker
(Caretaker物件)
class MusicSimulatorCareTaker {
constructor() {
/** @type {MusicSimulatorMemento[]} */
this.saves = [];
}
getUndo() {
if (this.saves.length === 0) {
return null;
} else {
return this.saves[this.saves.length - 1];
}
}
/** @param {MusicSimulatorMemento} */
setSave(memento) {
this.saves.push(memento);
}
}
测试,在模拟器上输入音符後,还原到上个状态:MusicSimulatorMementoSample
const musicSimulatorMementoSample = () => {
const musicSimulator = new MusicSimulator();
// 输入音阶
musicSimulator.inputSolfege(new Solfege("C"));
musicSimulator.inputSolfege(new Solfege("D"));
musicSimulator.inputSolfege(new Solfege("E"));
musicSimulator.inputSolfege(new Solfege("F"));
// 确认输入的音阶
musicSimulator.showInputs();
// 储存
const musicSimulatorCareTaker = new MusicSimulatorCareTaker();
musicSimulatorCareTaker.setSave(musicSimulator.saveInputs());
// 输入新的音阶
musicSimulator.inputSolfege(new Solfege("G"));
musicSimulator.inputSolfege(new Solfege("A"));
musicSimulator.inputSolfege(new Solfege("B"));
// 确认输入的音阶
musicSimulator.showInputs();
// 复原到上个状态
musicSimulator.restore(musicSimulatorCareTaker.getUndo());
// 确认输入的音阶
musicSimulator.showInputs();
}
musicSimulatorMementoSample();
Memento 模式常见的译名是备忘录,实际的行为的确与备忘录相似,将资料「暂存」在某个地方,这边是建立物件暂存於记忆体内。也因为是建立在记忆体内,一旦累积大量的记录,会对记忆体造成不小的负担,有可能会让程序当掉。这让我想起很久以前 Word 会忽然当掉,如果没有存档,那所有文字都消失了。
我在测试时注意到,「复原功能」、「取消复原功能」需要一个 Array(JS)、List(Java)来储存,如果逻辑没有设定好,可能在记录的排序上出问题,这点在设计上要多加注意。
总之,这是个用於特殊情况下,同时在开发上的要注意的细节较多的模式。
明天将介绍 Behavioural patterns 的第七个模式:Observer 模式。
<<: [Day23] Array methods 阵列操作方法(1)
>>: Alpine Linux Porting (2.11) clock is _sorta_ ticking
前言 在 OOP 的世界里,我们常常会听到高内聚(Cohesion),低耦合(Coupling),以...
一睁开眼,发现出现在自己眼前的是没见过的景色 这里是哪里... 一阵晕眩过後 对了我想起来了,前一...
先来张时代的眼泪 图片来源(官网资料) 最原始的实体主机一台一台设定环境:纯手工,因为硬体配置都是固...
仓库被堆放了哪些遗忘的记忆呢? 哪些人在使用迷你仓呢? 对於地狭人绸的城市来说,这样的存放空间是必要...
前面我们有说过,在Python的世界中,万物皆物件。但物件只是这个世界的最小单位而已,接下来让我们认...