文章目录
- 前言
- 思路
- 代码实现
- uml类图
- 总结
前言
最近学习设计模式行为型的模式,学到了备忘录模式提到这个模式可以记录一个对象的状态属性值,用于下次复用,于是便想到了我们在Windows系统上使用的撤销操作,于是便想着使用这个模式进行一次模仿复现
思路
以下是按照备忘录和命令模式结合的思路描述:
-
首先,我们有一个文档类
Document
,其中包含一个文本属性。文档类提供了设置和获取文本的方法。 -
我们引入备忘录类
Memento
,用于保存文档对象的状态。 -
文档类还实现了创建备忘录和恢复备忘录的方法。创建备忘录时,文档对象会将当前的文本状态传递给备忘录对象进行保存。恢复备忘录时,文档对象会从备忘录对象中获取之前保存的文本状态并恢复。
-
为了实现可逆操作和撤销功能,我们引入了命令接口
Command
,该接口定义了执行方法execute()
和撤销方法undo()
。 -
具体命令类
InsertTextCommand
是一个插入文本操作的具体实现。在执行命令时,该命令对象会调用文档对象的插入文本方法,并将执行前的文本状态保存到备忘录对象中。在撤销命令时,该命令对象会使用备忘录对象恢复文档的文本状态。 -
历史记录类
History
充当调用者的角色,用于记录执行的命令。它内部使用一个列表来保存命令对象。每次执行命令时,该命令对象被添加到列表中保存;每次撤销命令时,列表的最后一个命令对象被取出并执行其撤销操作。 -
在主程序中,我们实例化了文档对象、备忘录对象和历史记录对象。
-
执行插入文本命令1,创建插入文本命令对象并将其添加到历史记录对象的列表中。该命令对象会执行插入文本操作,并将执行前的文本状态保存到备忘录对象中。
-
执行插入文本命令2,同样创建插入文本命令对象并将其添加到历史记录对象的列表中。该命令对象也会执行插入文本操作,并将执行前的文本状态保存到备忘录对象中。
-
执行撤销命令,历史记录对象的列表中取出最后一个命令对象(即插入文本命令2),并执行其撤销操作。命令对象会从备忘录对象中获取之前保存的文本状态,恢复文档的内容。
-
输出文档的内容,即输出 “Hello”。
-
再次执行撤销命令,历史记录对象的列表中取出插入文本命令1,并执行其撤销操作。文档的内容变为空字符串。
-
输出文档的内容,即输出空字符串。
-
尝试再次执行撤销命令,由于历史记录中已没有可撤销的命令,不会执行任何操作。
-
最后,输出文档的内容,依然输出空字符串。
代码实现
实现类似于 Word 文档中的撤销和恢复操作,可以采用备忘录模式配合命令模式的方式。
- 备忘录类(Memento):备忘录类负责存储文档的状态。它可以保存文档的内容、样式、光标位置等信息。
class Memento {
private String content;
private String style;
private int cursorPosition;
// 构造函数和访问方法
}
- 命令接口(Command):命令接口定义执行和撤销操作的方法。
interface Command {
void execute();
void undo();
}
- 具体命令类(具体的操作):实现命令接口,执行和撤销文档的具体操作。例如,插入文本、修改样式、移动光标等。
class InsertTextCommand implements Command {
private Document document;
private Memento prevState;
private String newText;
public InsertTextCommand(Document document, String newText) {
this.document = document;
this.newText = newText;
}
public void execute() {
prevState = document.createMemento();
document.setText(newText);
}
public void undo() {
document.restore(prevState);
}
}
- 文档类(Originator):文档类维护文档的状态,并提供创建备忘录、恢复状态和执行操作的方法。
class Document {
private String text;
public void setText(String text) {
this.text = text;
}
public String getText() {
return text;
}
public Memento createMemento() {
return new Memento(text);
}
public void restore(Memento memento) {
text = memento.getState();
}
}
- 历史记录类(Caretaker):历史记录类负责存储备忘录对象,并管理执行和撤销操作的命令。
class History {
private Stack<Command> commandStack;
public History() {
commandStack = new Stack<>();
}
public void executeCommand(Command command) {
command.execute();
commandStack.push(command);
}
public void undo() {
if (!commandStack.isEmpty()) {
Command command = commandStack.pop();
command.undo();
}
}
}
使用以上设计的示例代码如下:
public static void main(String[] args) {
// 创建文档对象和历史记录对象
Document document = new Document();
History history = new History();
// 执行命令:插入文本
Command insertCommand1 = new InsertTextCommand(document, "Hello");
history.executeCommand(insertCommand1);
// 执行命令:插入文本
Command insertCommand2 = new InsertTextCommand(document, " World!");
history.executeCommand(insertCommand2);
// 输出文档内容
System.out.println(document.getText()); // 输出:World!
// 执行命令:撤销上一个命令
history.undo();
// 输出文档内容
System.out.println(document.getText()); // 输出:hello
// 执行命令:撤销上一个命令(没有可撤销的命令)
history.undo(); // 不执行任何操作
// 输出文档内容
System.out.println(document.getText()); // 输出:""
}
通过使用备忘录模式和命令模式,我们可以记录文档状态的变化,并在需要时进行撤销和恢复操作。每次执行操作时,都会创建对应的命令对象,并将其添加到历史记录中,以支持撤销和重做操作。
uml类图
总结
这个功能的实现使用了备忘录模式和命令模式两种设计模式。
备忘录模式用于保存文档对象的状态,并提供了恢复状态的功能。它将文档对象的状态封装在备忘录对象中,以便在需要时可以对其进行保存并恢复。这样,可以在不破坏文档对象封装性的情况下,实现文档对象的状态管理和回滚功能。
命令模式用于执行和撤销操作。通过将每个操作封装在一个命令对象中,并提供统一的执行和撤销方法,可以实现对操作的统一管理和控制。这样,可以方便地扩展和组合不同的操作,同时也解耦了调用者和接收者。
使用设计模式的好处包括:
-
提高代码的可维护性和可扩展性:设计模式使代码结构更清晰、更易于理解和维护。模式中定义了明确的角色和关系,使代码具有良好的组织结构和可扩展性。
-
复用性增加:设计模式通过提供通用的解决方案,使得代码可以在不同场景下被重复使用。这避免了重复编写相似的代码,提高了开发效率。
-
降低耦合度:设计模式通过明确角色和关系,将系统中各组件之间的依赖关系降到最低。这样,当需求变化或者需要修改某一个组件时,对其他组件的影响最小,易于维护和扩展。
-
提高代码的可测试性:设计模式将逻辑分离开来,使得每个模块可以独立地进行测试,便于编写单元测试和集成测试。