纪念品模式的缺点

Memento pattern drawbacks

所以,这是 Memento 模式的典型实现(跳过 getter 和 setter)。

public class Employee {
    private String name;
    private String phone;

    public EmployeeMemento save() {
        return new EmployeeMemento(name, phone);
    }

    public void revert(EmployeeMemento memento) {
        this.name = memento.getName();
        this.phone = memento.getPhone();
    }
}


  public class EmployeeMemento {
        private final String name;
        private final String phone;

        public EmployeeMemento(String name, String phone) {
            this.name = name;
            this.phone = phone;
        }
    }

public class Caretaker {
    private Stack<EmployeeMemento> history;

    public Caretaker() {
        history = new Stack<>();
    }

    public void save(Employee employee) {
        history.push(employee.save());
    }

    public void revert(Employee employee) {
        employee.revert(history.pop());
    }
}

我发现此模式的所有实现都或多或少与上面的相同。但是这种实现有一些问题,我不喜欢:

  1. 可以同时触发 employee.revert()caretaker.revert(employee)。我希望只有一个接入点。
  2. 如果我们想要更改 EmployeeMemento,我们还必须在 Employee class 中进行更改(因为 revert 方法)。

有办法克服这个问题吗? 又或者是我太在意了,这个细节不是那么重要?

1) 请注意,Caretaker 应该负责保管 Mementos,不一定负责 Undo/Redo。如果您查看 Internet 上的各种实现(例如 here),您会发现 Caretaker 没有 revert(),但通常类似于 getMemento()。所以负责撤消的 class 是另一个人,它在 Caretaker 上调用 getMemento(),然后在 Subject 上调用 revert()

即使您希望 Caretaker 负责撤消操作,请注意 employee.revert() 是一种专为 caretaker.revert() 使用而创建的方法,因为在此设计中,没有其他人可以访问纪念品。您可以将其可见性降低为仅由 Caretaker 可见。 (如果这是 C++,使用 friend 可以轻松完成,但在 Java 中,您必须发挥创意并使用 package 可见性或其他方式。)

2) 在 Memento 模式中,class 和它的 Memento 是紧耦合的。实际上只有 class 本身可以访问 Memento 的内部结构,没有其他人应该看到 Memento 是如何组成的。因此,对 class 的更改是否会传播到其 Memento.

并不重要

然后再来一次 如果你想隔离变化,你可以再次发挥创意。例如,不是在 Class 和它的 Memento 中复制 namephone,你可以提取另一个包含这些字段的 class(假设名称为 State) 然后在原来的 class 和它的 Memento 中使用这个 State。这样,当您更改 class 的状态时,您只需要修改 State.

旁注:最好将 Memento 定义为主题内的嵌套静态 class。

所以我的设计解决了您的问题,应该是这样的:

public class Employee {
    private State state;

    public Memento save() {
        return new Memento(state);
    }

    public void revert(Memento memento) {
        this.state = memento.state;
    }

    public static class Memento {
        private final State state;

        public Memento(State state) {
            this.state = state;
        }
    }

    public static class State {
        private String name;
        private String phone;
    }
}

public class Caretaker {
    private Stack<Employee.Memento> history;

    public Caretaker() {
        history = new Stack<>();
    }

    public void addMemento(Employee.Memento memento) {
        history.push(memento);
    }

    public Employee.Memento getMemento() {
        return history.pop();
    }
}

public class UndoHandler {
    Employee employee;
    Caretaker caretaker;

    public void snapshot() {
        caretaker.save(employee.save());
    }

    public void undo() {
        employee.revert(caretaker.getMemento());
    }
}