Как реализовать Memento Pattern в Котлине
В настоящее время я пытаюсь реализовать некоторые шаблоны проектирования в Kotlin в качестве упражнения, и я немного застрял в шаблоне "Memento". Мой справочный ресурс SourceMaking: Memento.
Я хочу реализовать эту структуру:
Следуя их "Контрольному списку"
- Определите роли "смотрителя" и "составителя".
- Создайте класс Memento и объявите создателя другом.
- Смотритель знает, когда "проверять точку" отправителя.
- Создатель создает Memento и копирует его состояние в этот Memento.
- Смотритель держит (но не может заглянуть в) Memento.
- Смотритель знает, когда нужно "откатить" виновника.
- Создатель восстанавливает себя, используя сохраненное состояние в 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
свойства класса с доступом на уровне пакета.