Перейти к основному содержимому

Шаблон проектирования Memento в Java

· 4 мин. чтения

1. Обзор

В этом уроке мы узнаем, что такое шаблон проектирования Memento и как его использовать.

Сначала немного пройдемся по теории. Затем мы создадим пример, в котором проиллюстрируем использование шаблона.

2. Что такое шаблон проектирования Memento?

Паттерн проектирования Memento, описанный Gang of Four в своей книге , — это поведенческий паттерн проектирования. Шаблон проектирования Memento предлагает решение для реализации невыполнимых действий. Мы можем сделать это, сохранив состояние объекта в данный момент и восстановив его, если действия, выполненные с тех пор, необходимо отменить.

На практике объект, состояние которого нужно сохранить, называется Первоисточником. Смотритель — это объект, запускающий сохранение и восстановление состояния, которое называется Мементо.

Объект Memento должен предоставлять Смотрителю как можно меньше информации. Это делается для того, чтобы мы не выставляли внутреннее состояние создателя внешнему миру, так как это нарушит принципы инкапсуляции. Однако создатель должен получить доступ к достаточному количеству информации, чтобы восстановить исходное состояние.

Давайте посмотрим на краткую диаграмму классов, иллюстрирующую, как различные объекты взаимодействуют друг с другом:

./562d4c17e2dda287027baa504e9b0c2f.png

Как мы видим, Создатель может производить и потреблять Сувениры. Между тем, смотритель только сохраняет состояние перед его восстановлением. Внутреннее представление Создателя скрыто от внешнего мира.

Здесь мы использовали одно поле для представления состояния инициатора, хотя мы не ограничены одним полем и могли использовать столько полей, сколько необходимо . Кроме того, состояние, хранящееся в объекте Memento, не обязательно должно совпадать с полным состоянием Создателя. Пока сохраненной информации достаточно для восстановления состояния Создателя, все готово.

3. Когда использовать шаблон дизайна Memento?

Как правило, шаблон проектирования Memento используется в ситуациях, когда некоторые действия невозможно отменить, поэтому требуется откат к предыдущему состоянию. Однако, если состояние создателя тяжелое, использование шаблона проектирования Memento может привести к дорогостоящему процессу создания и повышенному использованию памяти.

4. Пример паттерна «Помни»

4.1. Исходный образец

Давайте теперь посмотрим на пример шаблона проектирования Memento. Представим, что у нас есть текстовый редактор:

public class TextEditor {

private TextWindow textWindow;

public TextEditor(TextWindow textWindow) {
this.textWindow = textWindow;
}
}

Он имеет текстовое окно, в котором содержится текущий введенный текст, и предоставляет возможность добавить дополнительный текст:

public class TextWindow {

private StringBuilder currentText;

public TextWindow() {
this.currentText = new StringBuilder();
}

public void addText(String text) {
currentText.append(text);
}
}

4.2. Сувениры

Теперь давайте представим, что мы хотим, чтобы наш текстовый редактор реализовал некоторые функции сохранения и отмены. При сохранении мы хотим сохранить наш текущий текст. Таким образом, при отмене последующих изменений у нас будет восстановлен сохраненный текст.

Для этого мы воспользуемся шаблоном проектирования Memento. Во-первых, мы создадим объект, содержащий текущий текст окна:

public class TextWindowState {

private String text;

public TextWindowState(String text) {
this.text = text;
}

public String getText() {
return text;
}
}

Этот объект - наш сувенир. Как мы видим, мы решили использовать String вместо StringBuilder , чтобы предотвратить любое обновление текущего текста посторонними.

4.3. Создатель

После этого нам нужно будет предоставить классу TextWindow методы для создания и использования объекта Memento, что сделает TextWindow нашим источником:

private StringBuilder currentText;

public TextWindowState save() {
return new TextWindowState(currentText.toString());
}

public void restore(TextWindowState save) {
currentText = new StringBuilder(save.getText());
}

Метод save() позволяет нам создать объект, а метод restore() использует его для восстановления предыдущего состояния.

4.4. смотритель

Наконец, нам нужно обновить наш класс TextEditor . Как Хранитель, он сохранит состояние Создателя и попросит восстановить его при необходимости:

private TextWindowState savedTextWindow;

public void hitSave() {
savedTextWindow = textWindow.save();
}

public void hitUndo() {
textWindow.restore(savedTextWindow);
}

4.5. Тестирование решения

Давайте посмотрим, сработает ли это на пробном прогоне. Представьте, что мы добавляем какой-то текст в наш редактор, сохраняем его, затем добавляем еще и, наконец, отменяем. Для этого мы добавим метод print() в наш TextEditor , который возвращает строку текущего текста:

TextEditor textEditor = new TextEditor(new TextWindow());
textEditor.write("The Memento Design Pattern\n");
textEditor.write("How to implement it in Java?\n");
textEditor.hitSave();

textEditor.write("Buy milk and eggs before coming home\n");

textEditor.hitUndo();

assertThat(textEditor.print()).isEqualTo("The Memento Design Pattern\nHow to implement it in Java?\n");

Как мы видим, последнее предложение не является частью текущего текста, так как Memento был сохранен перед его добавлением.

5. Вывод

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

Полный код, использованный в этой статье, можно найти на GitHub .