3

私はMementoパターンについて調査を行っていますが、私が遭遇した例のほとんどは比較的類似しているようです(文字列を配列に保存し、必要に応じて復元します)。間違っている場合は修正しますが、今説明したメソッドは「オブジェクトのクローン作成」ですが、Mementoパターンを実装する他の方法は何ですか?

シリアル化についても取り上げましたが、オブジェクトのカプセル化に違反しているため、Mementoパターンに実装する方法ではないと言う人がいる灰色の領域があるようです。

では、誰もがパターンを実装する方法に光を当てることができるでしょうか?私の研究は、すべての異なるものの一種の混合物を考え出し、すべてを混乱させました。

ありがとう

4

2 に答える 2

4

Javaコレクションフレームワークはを定義しQueueます。これは役に立ちます。

候補コード:

public final class Memento<T>
{
    // List of saved values
    private final Queue<T> queue = new ArrayDeque<T>();

    // Last entered value, whether it has been saved or not
    private T currentValue;

    // No initial state, ie currentValue will be null on construction, hence
    // no constructor

    // Set a value, don't save it
    public void set(final T value)
    {
        currentValue = value;
    }

    // Persist the currently saved value
    public void persist()
    {
        queue.add(currentValue);
    }

    // Return the last saved value
    public T lastSaved()
    {
        return queue.element();
    }

    // Return the last entered value
    public T lastEntered()
    {
        return currentValue;
    }
}

このコードには特に欠けているものがたくさんありますが、簡単に実装できます。

  • 最後に保存した値に戻します。
  • nullのチェックはありません。
  • T実装していませんSerializable;
  • 便利な方法(値を追加して最後に保存された状態にするなど)。
  • コードはスレッドセーフではありません

等。

サンプルコード:

public static void main(final String... args)
{
    final Memento<String> memento = new Memento<String>();

    memento.set("state1");
    System.out.println(memento.lastEntered()); // "state1"
    memento.persist();
    memento.set("state2");
    System.out.println(memento.lastEntered()); // "state2"
    System.out.println(memento.lastSaved()); // "state1"
}

事実上、これは改善できるが、基礎として使用できる頭の痛い実装です-それを拡張することはあなたのニーズに依存します;)

于 2012-12-29T15:30:26.710 に答える
4

mementoの実装に伴う通常の問題は、さまざまな種類のオブジェクトの内部状態を表す多くのクラスが必要になることが多いことです。または、memento実装は、オブジェクトの状態を他の形式(たとえば、シリアル化されたJavaオブジェクト)にシリアル化する必要があります。

これは、クラスごとに特定のmementoクラスに依存しない、memento実装のスケッチです。このクラスの状態は、元に/やり直しのサポートのためにキャプチャされます。

最初に紹介する基本的な概念があります。

public interface Reference<T> {
    T get();
    void set(T value);
}

java.lang.ref.Referenceこのクラスはガベージコレクションを目的としているため、これはの抽象化です。しかし、それをビジネスロジックに使用する必要があります。基本的に、参照はフィールドをカプセル化します。したがって、これらは次のように使用することを目的としています。

public class Person {
    private final Reference<String> lastName;
    private final Reference<Date> dateOfBirth;

    // constructor ...

    public String getLastName() {
        return lastName.get();
    }

    public void setLastName(String lastName) {
        this.lastName.set(lastName);
    }

    public Date getDateOfBirt() {
        return dateOfBirth.get();
    }

    public void setDateOfBirth(Date dateOfBirth) {
        this.dateOfBirth.set(dateOfBirth);
    }
}

これらの参照を使用したオブジェクトのインスタンス化はそれほど簡単ではないかもしれませんが、ここでは省略していることに注意してください。

次に、memento実装の詳細を示します。

public interface Caretaker {

    void addChange(Change change);
    void undo();
    void redo();
    void checkpoint();   
}

public interface Change {

    Change createReversal();
    void revert();
}

基本的に、aChangeは、識別可能なオブジェクトの状態に対する単一の識別可能な変更を表します。AChangeはメソッドを呼び出すことrevertで元に戻すことができ、その変更の元に戻すには、createReversalメソッドで作成された変更を元に戻すことで元に戻すことができます。Caretakerメソッドを介してオブジェクトの状態への変更を累積しますaddChangeundoandredoメソッドを呼び出すことによりCaretaker、次のチェックポイントに到達するまで、すべての変更を元に戻すかやり直します(つまり、変更の元に戻す)。チェックポイントは、観察されたすべての変更が、変更されたすべてのオブジェクトのすべての状態を1つの有効な構成から別の有効な構成に変換する変更に蓄積されるポイントを表します。チェックポイントは通常、アクションの過去または前に作成されます。それらはを介して作成されますcheckpoint方法。

Caretakerそして今ここにwithを利用する方法がありますReference

public class ReferenceChange<T> implements Change {

    private final Reference<T> reference;
    private final T oldValue;
    private final T currentReferenceValue;

    public ReferenceChange(Reference<T> reference, T oldValue,
            T currentReferenceValue) {
        super();
        this.reference = reference;
        this.oldValue = oldValue;
        this.currentReferenceValue = currentReferenceValue;
    }

    @Override
    public void revert() {
        reference.set(oldValue);
    }

    @Override
    public Change createReversal() {
        return new ReferenceChange<T>(reference, currentReferenceValue,
                oldValue);
    }
}

public class CaretakingReference<T> implements Reference<T> {

    private final Reference<T> delegate;
    private final Caretaker caretaker;

    public CaretakingReference(Reference<T> delegate, Caretaker caretaker) {
        super();
        this.delegate = delegate;
        this.caretaker = caretaker;
    }

    @Override
    public T get() {
        return delegate.get();
    }

    @Override
    public void set(T value) {
        T oldValue = delegate.get();
        delegate.set(value);
        caretaker.addChange(new ReferenceChange<T>(delegate, oldValue, value));
    }
}

aの値がどのように変化Changeしたかを表すが存在します。ReferenceこれChangeは、が設定されたときに作成さCaretakingReferenceれます。この実装では、のを介して新しいをトリガーするべきではないため、実装Reference内にネストする必要があります。CaretakingReferencerevertReferenceChangeaddChangeCaretakingReference

コレクションプロパティはを使用する必要はありませんReference。その場合、世話をトリガーするカスタム実装を使用する必要があります。プリミティブはオートボクシングで使用できます。

この実装は、フィールドの代わりに常に参照を直接使用することにより、追加のランタイムとメモリのコストを推測します。

于 2013-01-07T19:14:26.230 に答える