TL; DR: Bạn có thể hỗ trợ hoàn tác và thực hiện lại các tác vụ bằng cách triển khai mẫu Command and Memento (Design Patterns - Gama et. al).
Các Memento Pattern
mô hình đơn giản này cho phép bạn lưu các bang của một đối tượng. Đơn giản chỉ cần bọc đối tượng trong một lớp mới và bất cứ khi nào trạng thái của nó thay đổi, hãy cập nhật nó.
public class Memento
{
MyObject myObject;
public MyObject getState()
{
return myObject;
}
public void setState(MyObject myObject)
{
this.myObject = myObject;
}
}
Các mẫu lệnh
Các mẫu Command lưu trữ các đối tượng gốc (mà chúng tôi muốn hỗ trợ undo/redo) và đối tượng vật lưu niệm, mà chúng ta cần trong trường hợp lùi lại.Hơn nữa, 2 phương pháp được định nghĩa:
- thực hiện: thực hiện lệnh
- unExecute: loại bỏ các lệnh
Code:
public abstract class Command
{
MyObject myObject;
Memento memento;
public abstract void execute();
public abstract void unExecute();
}
Các định nghĩa logic " Tác vụ "mở rộng Lệnh (ví dụ: Chèn):
public class InsertCharacterCommand extends Command
{
//members..
public InsertCharacterCommand()
{
//instantiate
}
@Override public void execute()
{
//create Memento before executing
//set new state
}
@Override public void unExecute()
{
this.myObject = memento.getState()l
}
}
Áp dụng các mô hình:
bước cuối cùng này quy định các hành vi undo/redo. Ý tưởng cốt lõi của họ là lưu trữ một loạt các lệnh hoạt động như một danh sách lịch sử của các lệnh. Để hỗ trợ làm lại, bạn có thể giữ một con trỏ thứ hai bất cứ khi nào lệnh hoàn tác được áp dụng. Lưu ý rằng bất cứ khi nào một đối tượng mới được chèn vào, thì tất cả các lệnh sau khi vị trí hiện tại của nó được loại bỏ; đó là đạt được bằng phương pháp deleteElementsAfterPointer
định nghĩa dưới đây:
private int undoRedoPointer = -1;
private Stack<Command> commandStack = new Stack<>();
private void insertCommand()
{
deleteElementsAfterPointer(undoRedoPointer);
Command command =
new InsertCharacterCommand();
command.execute();
commandStack.push(command);
undoRedoPointer++;
}
private void deleteElementsAfterPointer(int undoRedoPointer)
{
if(commandStack.size()<1)return;
for(int i = commandStack.size()-1; i > undoRedoPointer; i--)
{
commandStack.remove(i);
}
}
private void undo()
{
Command command = commandStack.get(undoRedoPointer);
command.unExecute();
undoRedoPointer--;
}
private void redo()
{
if(undoRedoPointer == commandStack.size() - 1)
return;
undoRedoPointer++;
Command command = commandStack.get(undoRedoPointer);
command.execute();
}
Kết luận:
Điều gì làm cho thiết kế này mạnh mẽ là một thực tế mà bạn có thể thêm bao nhiêu lệnh như bạn muốn (bằng cách mở rộng các lớp Command
) ví dụ , RemoveCommand
, UpdateCommand
và cứ tiếp tục như vậy. Hơn nữa, cùng một mẫu được áp dụng cho bất kỳ loại đối tượng nào, làm cho thiết kế có thể sử dụng lại và có thể sửa đổi trong các trường hợp sử dụng khác nhau.
Nguồn
2017-04-02 16:40:32
Hãy nhớ xem xét hỗ trợ hoàn tác được tích hợp sẵn; Tôi đã không bao giờ sử dụng nó và tôi không thể tìm thấy một hướng dẫn Swing cho nó, nhưng [ở đây] (http://docs.oracle.com/javase/6/docs/api/javax/swing/undo/UndoManager.html) là người quản lý. –