Как реализовать Memento Pattern в Котлине

В настоящее время я пытаюсь реализовать некоторые шаблоны проектирования в Kotlin в качестве упражнения, и я немного застрял в шаблоне "Memento". Мой справочный ресурс SourceMaking: Memento.

Я хочу реализовать эту структуру:

Диаграмма классов шаблона дизайна

Следуя их "Контрольному списку"

  1. Определите роли "смотрителя" и "составителя".
  2. Создайте класс Memento и объявите создателя другом.
  3. Смотритель знает, когда "проверять точку" отправителя.
  4. Создатель создает Memento и копирует его состояние в этот Memento.
  5. Смотритель держит (но не может заглянуть в) Memento.
  6. Смотритель знает, когда нужно "откатить" виновника.
  7. Создатель восстанавливает себя, используя сохраненное состояние в 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);
    }
}

Поскольку они являются внутренними классами, я могу читать частные поля Memento а также State от моего Originator экземпляр, но к Caretaker мой Memento Экземпляр полностью непрозрачен (только показывает Objectфункции члена).

Теперь, как мне реализовать это точное поведение в Kotlin? В основном мне не хватает функциональности чтения закрытых полей внутренних классов.

Самая близкая вещь, о которой я мог думать, была этим:

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 или.
Этот код, кстати, почти такой же, как и сгенерированный код, созданный функцией IntelliJ "Преобразовать Java в Kotlin", примененной к моему Java-коду (и он, очевидно, тоже не компилируется).

Так есть ли что-то очевидное (или волшебное), которого я здесь упускаю? Может быть, что-то, кроме структуры, отображаемой на диаграмме классов? Или эти точные спецификации не могут быть реализованы в Kotlin?

И еще одно замечание: является ли требование непрозрачности для объекта Memento фактически общеизвестным свойством паттерна Memento или SourceMaking придумала это требование?

2 ответа

Решение

Вы можете определить общедоступный родительский класс для Memento и класс частного наследника для него:

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

Класс реализации privateи снаружи Originatorэкземпляры будут рассматриваться только как Memento (см. сигнатуры функций), так что состояние не будет доступно.

Вы должны определить свой Memento свойства класса с доступом на уровне пакета.

Другие вопросы по тегам