web-dev-qa-db-ja.com

Javaのアクションに対して単純な元に戻す/やり直しを実装するにはどうすればよいですか?

XMLエディターを作成しましたが、最後のフェーズである元に戻す/やり直し機能の追加で立ち往生しています。

ユーザーが要素、属性、またはテキストをJTreeに追加する場合にのみ、元に戻す/やり直しを追加する必要があります。

私はまだこれに慣れていませんが、今日の学校では、元に戻すとやり直しと呼ばれる2つのスタックオブジェクト[]を作成し、実行されたアクションをそれらに追加しようとしました(失敗しました)。

たとえば、私は持っています:

Action AddElement() {

// some code
public void actionPerformed(ActionEvent e) {

                    performElementAction();
                }
}

performElementActionは、実際に要素をJTreeに追加するだけです。

実行したこのアクションを元に戻すスタックに追加する方法を追加したいと思います。元に戻す簡単な方法はありますか?プッシュ(実行されたアクション全体)または何か?

13
Chea Indian

TL; DR:コマンドパターンとMementoパターン(デザインパターン-Gamaet。al)を実装することで、元に戻すアクションとやり直しアクションをサポートできます。

Mementoパターン

この単純なパターンにより、オブジェクトの状態を保存できます。オブジェクトを新しいクラスでラップし、状態が変化するたびに更新するだけです。

public class Memento
{
    MyObject myObject;

    public MyObject getState()
    {
        return myObject;
    }

    public void setState(MyObject myObject)
    {
        this.myObject = myObject;
    }
}

コマンドパターン

コマンドパターンには、元のオブジェクト(元に戻す/やり直しをサポートする必要がある)と、元に戻す場合に必要なmementoオブジェクトが格納されます。さらに、2つのメソッドが定義されています。

  1. execute:コマンドを実行します
  2. unExecute:コマンドを削除します

コード:

public abstract class Command
{
    MyObject myObject;
    Memento memento;

    public abstract void execute();

    public abstract void unExecute();
}

コマンドを拡張する論理的な「アクション」を定義します(例:挿入):

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
    }
}

パターンの適用:

この最後のステップは、元に戻す/やり直しの動作を定義します。それらの中心的なアイデアは、コマンドの履歴リストとして機能するコマンドのスタックを格納することです。 REDOをサポートするために、元に戻すコマンドが適用されるたびに2次ポインターを保持できます。新しいオブジェクトが挿入されるたびに、現在の位置以降のすべてのコマンドが削除されることに注意してください。これは、以下に定義されているdeleteElementsAfterPointerメソッドによって実現されます。

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();
}

結論:

この設計を強力なものにしているのは、(Commandクラスを拡張することにより)必要な数のコマンドを追加できるという事実です。たとえば、RemoveCommandUpdateCommandなどです。さらに、同じパターンをあらゆるタイプのオブジェクトに適用できるため、さまざまなユースケースでデザインを再利用可能および変更可能にすることができます。

コマンドパターン を見てください。その用途には、元に戻す/やり直し機能の実装が含まれます。

7
Óscar López

この チュートリアル は、コマンドパターンの基本とSwingの元に戻す/やり直しのメカニズムを説明しています。それが役に立てば幸い。

1
Chris911

AddElementActionクラスがActionを継承して、Actionクラスを作成しようとしました。 AddElementActionには、それに応じて要素を追加/削除するDo()メソッドとUndo()メソッドを含めることができます。次に、元に戻す/やり直しのためにアクションの2つのスタックを保持し、ポップする前に一番上の要素でDo()/ Undo()を呼び出すだけです。

0
nolegs

You have to define undo(), redo() operations along with execute() in Command interface itself

例:

interface Command {

    void execute() ;

    void undo() ;

    void redo() ;
}

ConcreteCommandクラスで状態を定義します。 execute()メソッド後の現在の状態に応じて、コマンドをUndoStackとRedoStackのどちらに追加するかを決定し、それに応じて決定する必要があります。

理解を深めるために、この ndo-redo コマンドの記事をご覧ください。

0
Ravindra babu