Хранитель (шаблон проектирования)
Хранитель | |
---|---|
Memento | |
Тип | поведенческий |
Описан в Design Patterns | Да |
Хранитель (англ. Memento) — поведенческий шаблон проектирования, позволяющий, не нарушая инкапсуляцию, зафиксировать и сохранить внутреннее состояние объекта так, чтобы позднее восстановить его в это состояние.
Существует два возможных варианта реализации данного шаблона: классический, описанный в книге Design Patterns, и реже встречающийся нестандартный вариант.
Применение
[править | править код]Шаблон Хранитель используется, когда:
- необходимо сохранить снимок состояния объекта (или его части) для последующего восстановления
- прямой интерфейс получения состояния объекта раскрывает детали реализации и нарушает инкапсуляцию объекта
Структура
[править | править код]Классический вариант:
Нестандартный вариант:
- Originator — «Создатель»
- Caretaker — «Опекун»
- Memento — «Хранитель»
Описание
[править | править код]Классический вариант: Шаблон Хранитель используется двумя объектами: «Создателем» (originator) и «Опекуном» (caretaker). «Создатель» — это объект, у которого есть внутреннее состояние. Объект «Опекун» может производить некоторые действия с «Создателем», но при этом необходимо иметь возможность откатить изменения. Для этого «Опекун» запрашивает у «Создателя» объект «Хранителя». Затем выполняет запланированное действие (или последовательность действий). Для выполнения отката «Создателя» к состоянию, которое предшествовало изменениям, «Опекун» возвращает объект «Хранителя» его «Создателю». «Хранитель» является непрозрачным (то есть таким, который не может или не должен изменяться «Опекуном»).
Нестандартный вариант: Отличие данного варианта от классического заключено в более жёстком ограничении на доступ «Опекуна» к внутреннему состоянию «Создателя». В классическом варианте у «Опекуна» есть потенциальная возможность получить доступ к внутренним данным «Создателя» через «Хранителя», изменить состояние и установить его обратно «Создателю». В данном варианте «Опекун» обладает возможностью лишь восстановить состояние «Хранителя», вызвав Restore. Кроме всего прочего, «Опекуну» не требуется владеть связью на «Хранителя», чтобы восстановить его состояние. Это позволяет сохранять и восстанавливать состояние сложных иерархических или сетевых структур (состояния объектов и всех связей между ними) путём сбора снимков всех зарегистрированных объектов системы.
Примеры реализации
[править | править код]Стандартный вариант шаблона на Python
[править | править код]class Memento: def __init__(self, state: str): self.__state = state def get_state(self) -> str: return self.__state class Caretaker: def __init__(self, memento: Memento): self.__memento = memento def get_memento(self) -> Memento: return self.__memento class Originator: def __init__(self, state: str): self.__state = state @property def state(self) -> str: return self.__state @state.setter def state(self, state: str) -> None: self.__state = state def save_state(self) -> Memento: return Memento(self.__state) def restore_state(self, memento: Memento) -> None: self.__state = memento.get_state() if __name__ == '__main__': originator = Originator(state='on') print(originator.state) caretaker = Caretaker(originator.save_state()) originator.state = 'off' print(originator.state) originator.restore_state(caretaker.get_memento()) print(originator.state) # on # off # on
Стандартный вариант шаблона на Java
[править | править код]public class Memento { private final String state; public Memento(String state) { this.state = state; } public String getState() { return state; } } public class Caretaker { private Memento memento; public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento = memento; } } public class Originator { private String state; public void setState(String state) { this.state = state; } public String getState() { return state; } public Memento saveState() { return new Memento(state); } public void restoreState(Memento memento) { this.state = memento.getState(); } } public class Application { public static void main(String[] args) { Originator originator = new Originator(); Caretaker caretaker = new Caretaker(); originator.setState("on"); System.out.printf("State is %s\n", originator.getState()); caretaker.setMemento(originator.saveState()); originator.setState("off"); System.out.printf("State is %s\n", originator.getState()); originator.restoreState(caretaker.getMemento()); System.out.printf("State is %s\n", originator.getState()); } } /* * Output: * State is on * State is off * State is on */
Стандартный вариант шаблона на PHP5
[править | править код]<?php /** * Паттерн «Хранитель» хранит и восстанавливает состояния объекта */ namespace Memento { /** * Создатель сохраняет и восстанавливает внутреннее состояние */ class Originator { private $state; public function setState($state) { $this->state = $state; echo sprintf("State setted %s\n", $this->state); } public function getState() { return $this->state; } /** * Создать снимок состояния объекта * @return Memento */ public function saveMemento() { return new Memento($this->state); } /** * Восстановить состояние * @param \Memento\Memento $memento */ public function restoreMemento(Memento $memento) { echo sprintf("Restoring state...\n"); $this->state = $memento->getState(); } } /** * Снимок состояния */ class Memento { private $state; public function __construct($state) { $this->state = $state; } public function getState() { return $this->state; } } /** * Смотрящий (опекун) за состоянием объекта */ class Caretaker { private $memento; public function getMemento() { return $this->memento; } public function setMemento(Memento $memento) { $this->memento = $memento; } } $originator = new Originator(); $originator->setState("On"); // Store internal state $caretaker = new Caretaker(); $caretaker->setMemento($originator->saveMemento()); // Continue changing originator $originator->setState("Off"); // Restore saved state $originator->restoreMemento($caretaker->getMemento()); }
Первый вариант шаблона С#
[править | править код]//This structural code demonstrates the Memento pattern which temporary saves and restores another object's internal state. // Memento pattern -- Structural example using System; namespace DoFactory.GangOfFour.Memento.Structural { /// <summary> /// MainApp startup class for Structural /// Memento Design Pattern. /// </summary> class MainApp { /// <summary> /// Entry point into console application. /// </summary> static void Main() { Originator o = new Originator(); o.State = "On"; // Store internal state Caretaker c = new Caretaker(); c.Memento = o.CreateMemento(); // Continue changing originator o.State = "Off"; // Restore saved state o.SetMemento(c.Memento); // Wait for user Console.ReadKey(); } } /// <summary> /// The 'Originator' class /// </summary> class Originator { private string _state; // Property public string State { get { return _state; } set { _state = value; Console.WriteLine("State = " + _state); } } // Creates memento public Memento CreateMemento() { return (new Memento(_state)); } // Restores original state public void SetMemento(Memento memento) { Console.WriteLine("Restoring state..."); State = memento.State; } } /// <summary> /// The 'Memento' class /// </summary> class Memento { private string _state; // Constructor public Memento(string state) { this._state = state; } // Gets or sets state public string State { get { return _state; } } } /// <summary> /// The 'Caretaker' class /// </summary> class Caretaker { private Memento _memento; // Gets or sets memento public Memento Memento { set { _memento = value; } get { return _memento; } } } } Output State = On State = Off Restoring state: State = On
C#
[править | править код]using System; namespace MementoPatte { class Program { static void Main(string[] args) { Foo foo = new Foo("Test", 15); foo.Print(); Caretaker ct1 = new Caretaker(); Caretaker ct2 = new Caretaker(); ct1.SaveState(foo); foo.IntProperty += 152; foo.Print(); ct2.SaveState(foo); ct1.RestoreState(foo); foo.Print(); ct2.RestoreState(foo); foo.Print(); Console.ReadKey(); } } public interface IOriginator { object GetMemento(); void SetMemento(object memento); } public class Foo : IOriginator { public string StringProperty { get; private set; } public int IntProperty { get; set; } public Foo(string stringPropertyValue, int intPropertyValue = 0) { StringProperty = stringPropertyValue; IntProperty = intPropertyValue; } public void Print() { Console.WriteLine("============="); Console.WriteLine("StringProperty value: {0}",StringProperty); Console.WriteLine("IntProperty value: {0}",IntProperty); Console.WriteLine("============="); } object IOriginator.GetMemento() { return new Memento { StringProperty = this.StringProperty, IntProperty = this.IntProperty }; } void IOriginator.SetMemento(object memento) { if (Object.ReferenceEquals(memento, null)) throw new ArgumentNullException("memento"); if (!(memento is Memento)) throw new ArgumentException("memento"); StringProperty = ((Memento)memento).StringProperty; IntProperty = ((Memento)memento).IntProperty; } class Memento { public string StringProperty { get; set; } public int IntProperty { get; set; } } } public class Caretaker { private object m_memento; public void SaveState(IOriginator originator) { if (originator == null) throw new ArgumentNullException("originator"); m_memento = originator.GetMemento(); } public void RestoreState(IOriginator originator) { if (originator == null) throw new ArgumentNullException("originator"); if (m_memento == null) throw new InvalidOperationException("m_memento == null"); originator.SetMemento(m_memento); } } }
Нестандартный вариант шаблона
[править | править код]using System; using System.Collections.Generic; public interface IOriginator { IMemento GetState(); } public interface IShape : IOriginator { void Draw(); void Scale(double scale); void Move(double dx, double dy); } public interface IMemento { void RestoreState(); } public class CircleOriginator : IShape { private class CircleMemento : IMemento { private readonly double x; private readonly double y; private readonly double r; private readonly CircleOriginator originator; public CircleMemento(CircleOriginator originator) { this.originator = originator; x = originator.x; y = originator.y; r = originator.r; } public void RestoreState() { originator.x = x; originator.y = y; originator.r = r; } } double x; double y; double r; public CircleOriginator(double x, double y, double r) { this.x = x; this.y = y; this.r = r; } public void Draw() { Console.WriteLine("Circle with radius {0} at ({1}, {2})", r, x, y); } public void Scale(double scale) { r *= scale; } public void Move(double dx, double dy) { x += dx; y += dy; } public IMemento GetState() { return new CircleMemento(this); } } public class RectOriginator : IShape { private class RectMemento : IMemento { private readonly double x; private readonly double y; private readonly double w; private readonly double h; private readonly RectOriginator originator; public RectMemento(RectOriginator originator) { this.originator = originator; x = originator.x; y = originator.y; w = originator.w; h = originator.h; } public void RestoreState() { originator.x = x; originator.y = y; originator.w = w; originator.h = h; } } double x; double y; double w; double h; public RectOriginator(double x, double y, double w, double h) { this.x = x; this.y = y; this.w = w; this.h = h; } public void Draw() { Console.WriteLine("Rectangle {0}x{1} at ({2}, {3})", w, h, x, y); } public void Scale(double scale) { w *= scale; h *= scale; } public void Move(double dx, double dy) { x += dx; y += dy; } public IMemento GetState() { return new RectMemento(this); } } public class Caretaker { public static void Draw(IEnumerable<IShape> shapes) { foreach (IShape shape in shapes) { shape.Draw(); } } public static void MoveAndScale(IEnumerable<IShape> shapes) { foreach (IShape shape in shapes) { shape.Scale(10); shape.Move(3, 2); } } public static IEnumerable<IMemento> SaveStates(IEnumerable<IShape> shapes) { LinkedList<IMemento> states = new LinkedList<IMemento>(); foreach (IShape shape in shapes) { states.AddLast(shape.GetState()); } return states; } public static void RestoreStates(IEnumerable<IMemento> states) { foreach (IMemento state in states) { state.RestoreState(); } } public static void Main() { IShape[] shapes = { new RectOriginator(10, 20, 3, 5), new CircleOriginator(5, 2, 10) }; //Выводит: // Rectangle 3x5 at (10, 20) // Circle with radius 10 at (5, 2) Draw(shapes); //Сохраняем состояния фигур IEnumerable<IMemento> states = SaveStates(shapes); //Изменяем положение фигур MoveAndScale(shapes); //Выводит: // Rectangle 30x50 at (13, 22) // Circle with radius 100 at (8, 4) Draw(shapes); //Восстановление старого положения фигур RestoreStates(states); //Выводит: // Rectangle 3x5 at (10, 20) // Circle with radius 10 at (5, 2) Draw(shapes); } }
Нестандартный вариант шаблона на C++
[править | править код]# include <iostream> using namespace std; class Originator { int state; public: Originator(); class Memento; Memento* getMemento(); void setState(int); void dumpState(); class Memento { friend class Originator; private: int state; Originator* org; public: Memento(); void restoreState(); }; }; Originator::Originator(): state(0) { } void Originator::setState(int s) { state = s; } void Originator::dumpState() { cout << "State: " << state << endl; } Originator::Memento::Memento(): state(0) { } Originator::Memento* Originator::getMemento() { Originator::Memento* m = new Originator::Memento(); m->org = this; m->state = state; return m; } void Originator::Memento::restoreState() { org->state = state; } int main(void) { Originator org; org.setState(1); org.dumpState(); Originator::Memento* m1 = org.getMemento(); org.setState(2); org.dumpState(); m1->restoreState(); org.dumpState(); delete m1; }
Ссылки
[править | править код]- Паттерн Memento (хранитель) — назначение, описание, особенности и реализация на C++.