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
メソッドを介してオブジェクトの状態への変更を累積しますaddChange
。undo
andredo
メソッドを呼び出すことにより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
内にネストする必要があります。CaretakingReference
revert
ReferenceChange
addChange
CaretakingReference
コレクションプロパティはを使用する必要はありませんReference
。その場合、世話をトリガーするカスタム実装を使用する必要があります。プリミティブはオートボクシングで使用できます。
この実装は、フィールドの代わりに常に参照を直接使用することにより、追加のランタイムとメモリのコストを推測します。