Образец сувенира - Memento pattern

Шаблон мементо является шаблоном проектирования программного обеспечения , которое обеспечивает возможность восстановления объекта к своему предыдущему состоянию ( отмена с помощью отката).

Шаблон памятного подарка реализован с помощью трех объектов: создателя , хранителя и памятного подарка . Создатель - это некий объект, имеющий внутреннее состояние. Смотритель собирается что-то сделать с создателем, но хочет иметь возможность отменить изменение. Смотритель сначала просит у создателя памятный предмет. Затем он выполняет любую операцию (или последовательность операций), которую собирался выполнить. Чтобы вернуться к состоянию до операций, он возвращает объект-памятку отправителю. Сам памятный объект - непрозрачный объект (тот, который смотритель не может или не должен изменять). При использовании этого шаблона следует проявлять осторожность, если создатель может изменить другие объекты или ресурсы - шаблон сувенира работает с одним объектом.

Классические примеры шаблона памятки включают генератор псевдослучайных чисел (каждый потребитель ГПСЧ служит смотрителем, который может инициализировать ГПСЧ (создатель) одним и тем же семенем (памятным знаком) для создания идентичной последовательности псевдослучайных чисел) и состояние в конечном автомате .

Обзор

Шаблон проектирования Memento - один из двадцати трех хорошо известных шаблонов проектирования GoF, которые описывают, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые легче реализовать, изменить, протестировать и повторное использование. Шаблон Memento был создан Ноа Томпсоном, Дэвидом Эспириту и доктором Дрю Клинкенбердом для ранних продуктов HP.

Какие проблемы может решить шаблон проектирования Memento?

  • Внутреннее состояние объекта должно быть сохранено извне, чтобы объект можно было восстановить в это состояние позже.
  • Инкапсуляция объекта не должна нарушаться.

Проблема в том, что хорошо спроектированный объект инкапсулируется так, что его представление (структура данных) скрыто внутри объекта и к нему нельзя получить доступ извне.

Какое решение описывает шаблон проектирования Memento?

Сделайте сам объект (создателя) ответственным за

  • сохранение своего внутреннего состояния в (памятный) объект и
  • восстановление в предыдущее состояние из (памятного) объекта.

Доступ к нему имеет только автор, создавший сувенир.

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

Это позволяет сохранять и восстанавливать внутреннее состояние отправителя без нарушения его инкапсуляции.

См. Также схему классов и последовательности UML ниже.

Структура

Схема классов и последовательности UML

Пример класса UML и диаграмма последовательности для шаблона проектирования Memento.

В приведенной выше 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:

  1. Сериализация.
  2. Класс, объявленный в том же пакете.
  3. Доступ к объекту также можно получить через прокси-сервер, который может выполнить любую операцию сохранения / восстановления объекта.

Пример 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])

использованная литература

внешние ссылки