Образец сувенира - Memento pattern
Шаблон мементо является шаблоном проектирования программного обеспечения , которое обеспечивает возможность восстановления объекта к своему предыдущему состоянию ( отмена с помощью отката).
Шаблон памятного подарка реализован с помощью трех объектов: создателя , хранителя и памятного подарка . Создатель - это некий объект, имеющий внутреннее состояние. Смотритель собирается что-то сделать с создателем, но хочет иметь возможность отменить изменение. Смотритель сначала просит у создателя памятный предмет. Затем он выполняет любую операцию (или последовательность операций), которую собирался выполнить. Чтобы вернуться к состоянию до операций, он возвращает объект-памятку отправителю. Сам памятный объект - непрозрачный объект (тот, который смотритель не может или не должен изменять). При использовании этого шаблона следует проявлять осторожность, если создатель может изменить другие объекты или ресурсы - шаблон сувенира работает с одним объектом.
Классические примеры шаблона памятки включают генератор псевдослучайных чисел (каждый потребитель ГПСЧ служит смотрителем, который может инициализировать ГПСЧ (создатель) одним и тем же семенем (памятным знаком) для создания идентичной последовательности псевдослучайных чисел) и состояние в конечном автомате .
Обзор
Шаблон проектирования Memento - один из двадцати трех хорошо известных шаблонов проектирования GoF, которые описывают, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые легче реализовать, изменить, протестировать и повторное использование. Шаблон Memento был создан Ноа Томпсоном, Дэвидом Эспириту и доктором Дрю Клинкенбердом для ранних продуктов HP.
Какие проблемы может решить шаблон проектирования Memento?
- Внутреннее состояние объекта должно быть сохранено извне, чтобы объект можно было восстановить в это состояние позже.
- Инкапсуляция объекта не должна нарушаться.
Проблема в том, что хорошо спроектированный объект инкапсулируется так, что его представление (структура данных) скрыто внутри объекта и к нему нельзя получить доступ извне.
Какое решение описывает шаблон проектирования Memento?
Сделайте сам объект (создателя) ответственным за
- сохранение своего внутреннего состояния в (памятный) объект и
- восстановление в предыдущее состояние из (памятного) объекта.
Доступ к нему имеет только автор, создавший сувенир.
Клиент (смотритель) может запросить памятный знак у отправителя (чтобы сохранить внутреннее состояние отправителя) и передать памятный знак обратно отправителю (для восстановления предыдущего состояния).
Это позволяет сохранять и восстанавливать внутреннее состояние отправителя без нарушения его инкапсуляции.
См. Также схему классов и последовательности UML ниже.
Структура
Схема классов и последовательности UML
В приведенной выше UML - диаграмме класса , то Caretaker
класс относится к Originator
классу для сохранения ( createMemento()
) и восстановления ( restore(memento)
внутреннее состояния инициатора) в.
В Originator
классе реализует
(1) createMemento()
путь создания и возвращает Memento
объект, текущее внутреннее состояние МАГАЗИНОВ отправителя и
(2) restore(memento)
путь восстановления состояния из переданных в Memento
объекте.
В UML диаграмма последовательности
показывает время выполнения взаимодействия:
внутреннее состояние (1) Сохранение инициатора: В Caretaker
объектные вызовы createMemento()
на Originator
объект, который создает Memento
объект, сохраняет его текущее внутреннее состояние ( setState()
), и возвращает Memento
к Caretaker
.
Внутреннее состояние (2) Восстановление инициатора: В Caretaker
вызовы restore(memento)
на Originator
объекте и указывает Memento
объект , который хранит состояние , которое должно быть восстановлено. Объект Originator
получает state ( getState()
) из, Memento
чтобы установить собственное состояние.
Пример Java
Следующая программа на Java иллюстрирует использование шаблона «отменить».
import java.util.List;
import java.util.ArrayList;
class Originator {
private String state;
// The class could also contain additional data that is not part of the
// state saved in the memento..
public void set(String state) {
this.state = state;
System.out.println("Originator: Setting state to " + state);
}
public Memento saveToMemento() {
System.out.println("Originator: Saving to Memento.");
return new Memento(this.state);
}
public void restoreFromMemento(Memento memento) {
this.state = memento.getSavedState();
System.out.println("Originator: State after restoring from Memento: " + state);
}
public static class Memento {
private final String state;
public Memento(String stateToSave) {
state = stateToSave;
}
// accessible by outer class only
private String getSavedState() {
return state;
}
}
}
class Caretaker {
public static void main(String[] args) {
List<Originator.Memento> savedStates = new ArrayList<Originator.Memento>();
Originator originator = new Originator();
originator.set("State1");
originator.set("State2");
savedStates.add(originator.saveToMemento());
originator.set("State3");
// We can request multiple mementos, and choose which one to roll back to.
savedStates.add(originator.saveToMemento());
originator.set("State4");
originator.restoreFromMemento(savedStates.get(1));
}
}
Результат:
Originator: Setting state to State1 Originator: Setting state to State2 Originator: Saving to Memento. Originator: Setting state to State3 Originator: Saving to Memento. Originator: Setting state to State4 Originator: State after restoring from Memento: State3
В этом примере в качестве состояния используется строка, которая в Java является неизменяемым объектом . В реальных сценариях состояние почти всегда будет изменяемым объектом, и в этом случае необходимо сделать копию состояния.
Надо сказать, что у показанной реализации есть недостаток: в ней объявляется внутренний класс. Было бы лучше, если бы эта стратегия сувениров могла применяться более чем к одному отправителю.
Есть три других способа получить Memento:
- Сериализация.
- Класс, объявленный в том же пакете.
- Доступ к объекту также можно получить через прокси-сервер, который может выполнить любую операцию сохранения / восстановления объекта.
Пример C #
Шаблон памятки позволяет фиксировать внутреннее состояние объекта без нарушения инкапсуляции, так что позже можно отменить / отменить изменения, если потребуется. Здесь можно увидеть, что объект-сувенир фактически используется для отмены изменений, внесенных в объект.
class Memento
{
private readonly string savedState;
private Memento(string stateToSave)
{
savedState = stateToSave;
}
public class Originator
{
private string state;
// The class could also contain additional data that is not part of the
// state saved in the memento.
public void Set(string state)
{
Console.WriteLine("Originator: Setting state to " + state);
this.state = state;
}
public Memento SaveToMemento()
{
Console.WriteLine("Originator: Saving to Memento.");
return new Memento(state);
}
public void RestoreFromMemento(Memento memento)
{
state = memento.savedState;
Console.WriteLine("Originator: State after restoring from Memento: " + state);
}
}
}
class Caretaker
{
static void Main(string[] args)
{
List<Memento> savedStates = new List<Memento>();
Memento.Originator originator = new Memento.Originator();
originator.Set("State1");
originator.Set("State2");
savedStates.Add(originator.SaveToMemento());
originator.Set("State3");
// We can request multiple mementos, and choose which one to roll back to.
savedStates.Add(originator.SaveToMemento());
originator.Set("State4");
originator.RestoreFromMemento(savedStates[1]);
}
}
Пример Python
"""
Memento pattern example.
"""
class Memento:
def __init__(self, state) -> None:
self._state = state
def get_saved_state(self):
return self._state
class Originator:
_state = ""
def set(self, state) -> None:
print("Originator: Setting state to", state)
self._state = state
def save_to_memento(self) -> Memento:
print("Originator: Saving to Memento.")
return Memento(self._state)
def restore_from_memento(self, memento) -> None:
self._state = memento.get_saved_state()
print("Originator: State after restoring from Memento:", self._state)
saved_states = []
originator = Originator()
originator.set("State1")
originator.set("State2")
saved_states.append(originator.save_to_memento())
originator.set("State3")
saved_states.append(originator.save_to_memento())
originator.set("State4")
originator.restore_from_memento(saved_states[1])
использованная литература
внешние ссылки
- Описание Memento Pattern в Ada
- Схема классов Memento UML с примерами кода C # и .NET
- SourceMaking Учебник
- Шаблон дизайна Memento с использованием Java