在Java中实现Memento模式的不同方法

时间:2012-12-29 15:02:29

标签: java design-patterns memento

我正在对Memento模式进行一些研究,似乎我遇到的大多数示例似乎都相对相似(将一个字符串保存到数组中并在需要时恢复它)现在纠正我,如果我错了但是我相信我刚才描述的方法是“对象克隆”,但实现Memento模式的其他方法是什么?

我可以使用序列化中提到的内容,但似乎有一个灰色区域,人们说它违反了对象的封装,并且不是因为这个而实现Memento Pattern的方法。 / p>

那么任何人都可以对实施模式的方法有所了解吗?我的研究得出了各种不同的东西,并且让一切变得混乱。

由于

2 个答案:

答案 0 :(得分:4)

Java Collections框架定义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;
    }
}

这段代码显然遗漏了很多东西,但很容易实现:

  • 恢复为上次保存的值;
  • 不检查空值;
  • 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"
}

实际上:这是一个可以改进的脑卒中实现,但可以作为基础 - 扩展它取决于你的需要;)

答案 1 :(得分: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);
    }
}

请注意,使用这些引用进行对象实例化可能不是那么简单,但我们将其留在这里。

现在,以下是纪念品实施的详细信息:

public interface Caretaker {

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

public interface Change {

    Change createReversal();
    void revert();
}

基本上Change表示对可识别对象状态的单个可识别更改。通过调用Change方法可以恢复revert,并且可以通过还原createReversal方法创建的更改来恢复该更改的撤消。 Caretaker通过addChange方法累积对象状态的更改。通过调用undoredo方法,Caretaker恢复或重做(即恢复更改的反转)所有更改,直到达到下一个检查点。检查点表示所有观察到的更改将累积到一个点,该更改将所有已更改对象的所有状态从一个有效配置转换为另一个有效配置。检查点通常在操作之前或之后创建。这些是通过checkpoint方法创建的。

现在,这里是如何使用CaretakerReference

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

存在Change,表示Reference的值如何变化。设置Change时会创建此CaretakingReference。在此实现中,Reference实现中需要嵌套CaretakingReference,因为revert的{​​{1}}不应通过ReferenceChange触发新的addChange CaretakingReference

集合属性无需使用Reference。在这种情况下,应该使用触发看护的自定义实现。基元可以与自动装箱一起使用。

此实现通过始终直接使用引用而不是字段来推断额外的运行时和内存开销。

相关问题