实现可撤消命令-Java
我目前正在编写一个小的文本编辑器(学校项目),在找到一个好的、干净的方法来管理可撤销的命令时遇到了一些问题 (这不是一个代码审查问题,因为这不仅仅是关于改进。如果我想让我的应用程序按我所希望的那样工作,我需要更改我的代码) 下面是我的应用程序的工作原理:我有一个具体的主题,它包含缓冲区,实际上是一个字符数组列表。每当修改此arraylist(插入、剪切、粘贴…)时,主题都会更新观察者(目前仅包含在一个gui中)(MVC模式) 到目前为止,我对撤消和重做所做的是保存缓冲区的整个状态(通过memento),这很好,但是:实现可撤消命令-Java,java,model-view-controller,design-patterns,memento,Java,Model View Controller,Design Patterns,Memento,我目前正在编写一个小的文本编辑器(学校项目),在找到一个好的、干净的方法来管理可撤销的命令时遇到了一些问题 (这不是一个代码审查问题,因为这不仅仅是关于改进。如果我想让我的应用程序按我所希望的那样工作,我需要更改我的代码) 下面是我的应用程序的工作原理:我有一个具体的主题,它包含缓冲区,实际上是一个字符数组列表。每当修改此arraylist(插入、剪切、粘贴…)时,主题都会更新观察者(目前仅包含在一个gui中)(MVC模式) 到目前为止,我对撤消和重做所做的是保存缓冲区的整个状态(通过memen
- 显然可以改进
- 不适用于我需要实现的新功能(记录用户操作以便他可以随时播放)
...
class KeyboardListener extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
...
commandManager.executeCommandInsert(caretStart, caretStop, e.getKeyChar());
...
}
...
}
...
public void executeCommandInsert(int start, int end, char character) {
addMementoUndo();
commandInsert.setCarets(start, end);
commandInsert.setChar(character);
commandInsert.execute();
}
public void addMementoUndo()
{
TextConcrete text = TextConcrete.getInstance();
this.commandsUndo.add(0, text.createMemento());
if(recordMode){
this.recordings.add(text.createMemento());
}
}
...
...
public void execute(){
TextConcrete text = TextConcrete.getInstance();
text.insert(this.start, this.end, this.character);
}
...
...
public void insert(int start, int end, char character){
//inserting in ArrayList
}
public CommandUndoable createMemento(){
CommandUndoable mem = new CommandUndoable();
mem.setState(getState());
return mem;
}
public String getState(){
StringBuilder temp = new StringBuilder();
for(int idx = 0; idx < this.state.size(); idx++){
temp.append(this.state.get(idx));
}
return temp.toString();
}
...
CommandManager.java
...
class KeyboardListener extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
...
commandManager.executeCommandInsert(caretStart, caretStop, e.getKeyChar());
...
}
...
}
...
public void executeCommandInsert(int start, int end, char character) {
addMementoUndo();
commandInsert.setCarets(start, end);
commandInsert.setChar(character);
commandInsert.execute();
}
public void addMementoUndo()
{
TextConcrete text = TextConcrete.getInstance();
this.commandsUndo.add(0, text.createMemento());
if(recordMode){
this.recordings.add(text.createMemento());
}
}
...
...
public void execute(){
TextConcrete text = TextConcrete.getInstance();
text.insert(this.start, this.end, this.character);
}
...
...
public void insert(int start, int end, char character){
//inserting in ArrayList
}
public CommandUndoable createMemento(){
CommandUndoable mem = new CommandUndoable();
mem.setState(getState());
return mem;
}
public String getState(){
StringBuilder temp = new StringBuilder();
for(int idx = 0; idx < this.state.size(); idx++){
temp.append(this.state.get(idx));
}
return temp.toString();
}
...
CommandInsert.java
...
class KeyboardListener extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
...
commandManager.executeCommandInsert(caretStart, caretStop, e.getKeyChar());
...
}
...
}
...
public void executeCommandInsert(int start, int end, char character) {
addMementoUndo();
commandInsert.setCarets(start, end);
commandInsert.setChar(character);
commandInsert.execute();
}
public void addMementoUndo()
{
TextConcrete text = TextConcrete.getInstance();
this.commandsUndo.add(0, text.createMemento());
if(recordMode){
this.recordings.add(text.createMemento());
}
}
...
...
public void execute(){
TextConcrete text = TextConcrete.getInstance();
text.insert(this.start, this.end, this.character);
}
...
...
public void insert(int start, int end, char character){
//inserting in ArrayList
}
public CommandUndoable createMemento(){
CommandUndoable mem = new CommandUndoable();
mem.setState(getState());
return mem;
}
public String getState(){
StringBuilder temp = new StringBuilder();
for(int idx = 0; idx < this.state.size(); idx++){
temp.append(this.state.get(idx));
}
return temp.toString();
}
...
textcontracte.java
...
class KeyboardListener extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
...
commandManager.executeCommandInsert(caretStart, caretStop, e.getKeyChar());
...
}
...
}
...
public void executeCommandInsert(int start, int end, char character) {
addMementoUndo();
commandInsert.setCarets(start, end);
commandInsert.setChar(character);
commandInsert.execute();
}
public void addMementoUndo()
{
TextConcrete text = TextConcrete.getInstance();
this.commandsUndo.add(0, text.createMemento());
if(recordMode){
this.recordings.add(text.createMemento());
}
}
...
...
public void execute(){
TextConcrete text = TextConcrete.getInstance();
text.insert(this.start, this.end, this.character);
}
...
...
public void insert(int start, int end, char character){
//inserting in ArrayList
}
public CommandUndoable createMemento(){
CommandUndoable mem = new CommandUndoable();
mem.setState(getState());
return mem;
}
public String getState(){
StringBuilder temp = new StringBuilder();
for(int idx = 0; idx < this.state.size(); idx++){
temp.append(this.state.get(idx));
}
return temp.toString();
}
...
。。。
公共void插入(整数开始、整数结束、字符){
//插入ArrayList
}
公共命令可撤消createMemento(){
CommandUndoable mem=新CommandUndoable();
mem.setState(getState());
返回mem;
}
公共字符串getState(){
StringBuilder temp=新的StringBuilder();
for(int idx=0;idx
CommandUndoable只是保存缓冲区状态的纪念品,然后保存在管理员(CommandManager)的纪念品列表中
非常感谢您的帮助。您可以创建一个可由命令实现的可撤销接口,例如:
public interface Undoable {
public void do(Editor text)
public void undo(Editor text)
}
其中编辑器是编辑器的模型,您可以在其中插入文本、从中删除文本或使用其他命令进行操作。实现接口的命令只需要知道如何“执行”和“撤消”本身,因此您不需要在每个步骤中存储文本缓冲区的副本,只需存储命令列表。我更喜欢定义两个接口,但在它们的方法中没有任何参数
public interface Command {
public void execute();
}
。。。对于撤消操作:
public interface UndoableCommand extends Command {
public void undo();
}
我认为,这更灵活,因为您的应用程序中可能存在一些无法撤消的交互。(如保存或加载文件)
要实现这样一个UndoableCommand
,只需编写这样的代码即可
public class EditorInsertCommand implements UndoableCommand {
private Editor editor;
private String textToInsert;
private String textBefore;
public EditorInsertCommand(Editor editor, String textToInsert){
this.editor = editor;
this.textToInsert = textToInsert;
}
public void execute() {
this.textBefore = editor.getText();
editor.setText(textToInsert);
};
public void undo(){
editor.setText(textBefore );
};
}
execute
和undo
的处理也可以通过CommandManager
完成。是的,但您不是仍然使用该方法保存整个缓冲区吗?(textfore
)您可以自由地在逻辑上实现这一点。这甚至是最简单的方法。我只是想展示一下命令
和可撤销命令
界面的用法。没错,他就是。因此,我不会保存整个文本,而是保存插入发生的位置和插入的文本。因此,撤销应该从修改后的文本中剪切插入的文本,而不是替换整个文本。然后您只需将此
保存在列表中?有一个单独的CommandHandler类来处理撤销/重做机制。我在Mark Grand的《Java模式》一书中读到一个类似于您的解决方案,我只是稍微修改了一下它正在工作。谢谢你的帮助。有很多不同的方法来实现撤销/重做/重放。最常见的一种是使用“命令”设计模式,您可能想了解它(您提到了“memento”,但不清楚您是否指的是设计模式,因为memento模式通常不是用于撤消/重做/重放的模式)。如果/当你在不可变对象上执行OO时,你可以用一种更简单的方式实现和撤销/重做,只需简单地保存整个状态,但这会“浪费”内存。是的,我使用的是命令模式(命令界面,然后所有命令都作为单独的对象)。memento是保存整个缓冲区的方法,但是现在我对如何创建一个新的实现有些迷茫。