Jump to content

Сувенирный узор

Шаблон «память» — это шаблон проектирования программного обеспечения , который раскрывает частное внутреннее состояние объекта. Одним из примеров того, как это можно использовать, является восстановление объекта в его предыдущее состояние (отмена посредством отката), другой — управление версиями, третий — пользовательская сериализация.

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

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

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

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

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

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

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

[ редактировать ]

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

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

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

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

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

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

Структура

[ редактировать ]

Класс UML и диаграмма последовательности

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

На приведенной выше диаграмме классов 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 получает состояние ( 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. Доступ к объекту также можно получить через прокси-сервер, который может выполнить любую операцию сохранения/восстановления объекта.

пример С#

[ редактировать ]

Шаблон «память» позволяет фиксировать внутреннее состояние объекта, не нарушая инкапсуляцию, так что позже при необходимости можно отменить/вернуть изменения. Здесь можно видеть, что объект Memento фактически используется для отмены изменений, внесенных в объект.

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)
    {
        var savedStates = new List<Memento>();

        var 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 Originator:
    _state = ""

    def set(self, state: str) -> None:
        print(f"Originator: Setting state to {state}")
        self._state = state

    def save_to_memento(self) -> "Memento":
        return self.Memento(self._state)

    def restore_from_memento(self, m: "Memento") -> None:
        self._state = m.get_saved_state()
        print(f"Originator: State after restoring from Memento: {self._state}")

    class Memento:

        def __init__(self, state):
            self._state = state

        def get_saved_state(self):
            return 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])

Пример Javascript

[ редактировать ]
// The Memento pattern is used to save and restore the state of an object.
// A memento is a snapshot of an object's state.
var Memento = {// Namespace: Memento
    savedState : null, // The saved state of the object.

    save : function(state) { // Save the state of an object.
        this.savedState = state;
    },

    restore : function() { // Restore the state of an object.
        return this.savedState;
    }
};

// The Originator is the object that creates the memento.
// defines a method for saving the state inside a memento.
var Originator = {// Namespace: Originator
        state : null, // The state to be stored

        // Creates a new originator with an initial state of null
        createMemento : function() { 
            return {
                state : this.state // The state is copied to the memento.
            };
        },
        setMemento : function(memento) { // Sets the state of the originator from a memento
            this.state = memento.state;
        }
    };


// The Caretaker stores mementos of the objects and
// provides operations to retrieve them.
var Caretaker = {// Namespace: Caretaker
        mementos : [], // The mementos of the objects.
        addMemento : function(memento) { // Add a memento to the collection.
            this.mementos.push(memento);
        },
        getMemento : function(index) { // Get a memento from the collection.
            return this.mementos[index];
        }
    };

var action_step = "Foo"; // The action to be executed/the object state to be stored.
var action_step_2 = "Bar"; // The action to be executed/the object state to be stored.

// set the initial state
Originator.state = action_step;
Caretaker.addMemento(Originator.createMemento());// save the state to the history
console.log("Initial State: " + Originator.state); // Foo

// change the state
Originator.state = action_step_2;
Caretaker.addMemento(Originator.createMemento()); // save the state to the history
console.log("State After Change: " + Originator.state); // Bar

// restore the first state - undo
Originator.setMemento(Caretaker.getMemento(0));
console.log("State After Undo: " + Originator.state); // Foo

// restore the second state - redo
Originator.setMemento(Caretaker.getMemento(1));
console.log("State After Redo: " + Originator.state); // Bar
  1. ^ «Шаблон проектирования Memento — структура и сотрудничество» . w3sDesign.com . Проверено 12 августа 2017 г.
[ редактировать ]
  • Описание шаблона Memento в Ada
  • Диаграмма классов Memento UML с примерами кода C# и .NET
  • Учебное пособие по созданию исходного кода
  • Шаблон проектирования Memento с использованием Java
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 83c77eab045179a75f66724b0db4b509__1716401580
URL1:https://arc.ask3.ru/arc/aa/83/09/83c77eab045179a75f66724b0db4b509.html
Заголовок, (Title) документа по адресу, URL1:
Memento pattern - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)