如何在 Kotlin 中实现 Memento Pattern

How to implement Memento Pattern in Kotlin

我目前正在尝试在 Kotlin 中实现一些设计模式作为练习,但我有点受困于 'Memento' 模式。我的参考资源是SourceMaking: Memento.

我想实现这个结构:

在关注他们的同时 "Checklist"

  1. 确定“看管人”和“发起人”的角色。
  2. 创建一个纪念品class并声明发起者为好友。
  3. 管理员知道什么时候 "check point" 发起人。
  4. Originator 创建一个 Memento 并将其状态复制到该 Memento。
  5. Caretaker 拿着 (但无法窥视) 纪念品。
  6. 管理员知道什么时候 "roll back" 发起人。
  7. Originator 使用 Memento 中保存的状态恢复自身。

我无法执行第 5 步。如何制作一个 Memento 对象,其字段可以从 Originator 实例内部读取,但对 Caretaker 完全不透明?

我已经在 Java 中成功实现了如下:

public class Originator {

    private final int id;
    private String title;
    private String description;

    public Originator(int id) {
        this.id = id;
    }

    /* skipping title and description getter & setter */

    public Memento saveState() {
        return new Memento(new State(id, title, description));
    }

    public void restore(Memento memento) {
        id = memento.state.id;
        title = memento.state.title;
        description = memento.state.description;
    }

    private class State {

        private final int id;
        private final String title;
        private final String description;

        public State(int id, String title, String description) {
            this.id = id;
            this.title = title;
            this.description = description;
        }
    }

    public class Memento {

        private final State state;

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

还有一个看门人

public class Caretaker {

    public Originator originator;

    public Caretaker(@NotNull Originator originator) {
        this.originator = originator;
    }

    public Originator.Memento save() {
        return originator.saveState();
    }

    public void restore(@NotNull Originator.Memento memento) {
        originator.restoreFromState(memento);
    }
}

因为它们是内部 classes 我可以从我的 Originator 实例中读取 MementoState 的私有字段,但是到 Caretaker我的 Memento 实例是完全不透明的(只显示 Objects 成员函数)。

现在我该如何在 Kotlin 中实现这个确切的行为?基本上我缺少读取内部 classes 的私有字段的功能。

我能想到的最接近的事情是:

class Originator(id: Long) {

    private var id: Long = id
    var description: String = ""
    var title: String = ""

    fun saveState() = Memento(State(id, title, description))

    fun restoreState(memento: Memento) {
        id = memento.state.id // <-- cannot access 'state': it is private in 'Memento'
        title = memento.state.title // <-- cannot access 'state': it is private in 'Memento'
        description = memento.state.description // <-- cannot access 'state': it is private in 'Memento'
    }

    inner class State(private val id: Long,
                  private val title: String,
                  private val description: String)

    inner class Memento(private val state: State)
}

这具有 Memento 对我的 Caretaker 实例完全不透明的预期效果,但我也无法从 Originator 中读取字段。
顺便说一下,这段代码与应用于我的 Java 代码的 IntelliJ 的 'Convert Java to Kotlin' 功能生成的代码几乎完全相同(而且它显然也无法编译)。

那么我在这里缺少什么明显的(或神奇的)东西吗?可能不是 class 图中显示的结构?或者这些确切的规范不能在 Kotlin 中实现吗?

另外请注意:Memento 对象的不透明性要求实际上是通俗接受的 属性 Memento 模式还是 SourceMaking 提出了这个要求?

您应该定义具有包级访问权限的 Memento class 属性。

您可以为 Memento 定义一个 public 父 class 并为其定义一个私有继承者 class:

class Originator {
    /* irrelevant declarations skipped */

    abstract inner class Memento

    private inner class MementoImpl(val state: State) : Memento()

    fun saveState(): Memento {
        return MementoImpl(State(id, title, description))
    }

    fun restore(memento: Memento) {
        memento as MementoImpl
        id = memento.state.id
        title = memento.state.title
        description = memento.state.description
    }
}

实现 class 是 private,并且在 Originator 之外,实例只会被视为 Memento(参见函数签名),因此状态将无法访问。