- Хранитель (шаблон проектирования)
-
Шаблон проектирования Хранитель Memento Тип: поведенческий
Описан в Design Patterns Да
Хранитель (также известный как Memento, Token, Лексема) — поведенческий шаблон проектирования.
Позволяет, не нарушая инкапсуляцию, зафиксировать и сохранить внутреннее состояния объекта так, чтобы позднее восстановить его в этом состоянии.
Существует два возможных варианта реализации данного шаблона: классический, описанный в книге Design Patterns, и реже встречаемый нестандартный вариант.
Содержание
Применение
Шаблон Хранитель используется, когда:
- необходимо сохранить снимок состояния объекта (или его части) для последующего восстановления
- прямой интерфейс получения состояния объекта раскрывает детали реализации и нарушает инкапсуляцию объекта
Структура
Классический вариант:
Нестандартный вариант:
- Originator - "Создатель"
- Caretaker - "Опекун"
- Memento - "Хранитель"
Описание
Классический вариант: Шаблон Хранитель используется двумя объектами: "Создателем" (originator) и "Опекуном" (caretaker). "Создатель" - это объект, у которого есть внутреннее состояние. Объект "Опекун" может производить некоторые действия с "Создателем", но при этом необходимо иметь возможность откатить изменения. Для этого "Опекун" запрашивает у "Создателя" объект "Хранителя". Затем выполняет запланированное действие (или последовательность действий). Для выполнения отката "Создателя" к состоянию, которое предшествовало изменениям, "Опекун" возвращает объект "Хранителя" его "Создателю". "Хранитель" является непрозрачным (т.е. таким, который не может или не должен изменяться "Опекуном").
Нестандартный вариант: Отличие данного варианта от классического заключено в более жёстком ограничении на доступ "Опекуна" к внутреннему состоянию "Создателя". В классическом варианте у "Опекуна" есть потенциальная возможность получить доступ к внутренним данным "Создателя" через "Хранителя", изменить состояние и установить его обратно "Создателю". В данном варианте "Опекун" обладает возможностью лишь восстановить состояние "Хранителя", вызвав Restore. Кроме всего прочего, "Опекуну" не требуется владеть связью на "Хранителя", чтобы восстановить его состояние. Это позволяет сохранять и восстанавливать состояние сложных иерархических или сетевых структур (состояния объектов и всех связей между ними) путём сбора снимков всех зарегистрированных объектов системы.
Примеры реализации
Стандартный вариант шаблона на PHP5
Исходный текст на языке 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 CreateMemento() { return new Memento($this->state); } /** * Восстановить состояние * @param \Memento\Memento $memento */ public function setMemento(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->CreateMemento()); // Continue changing originator $originator->setState("Off"); // Restore saved state $originator->setMemento($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); } } }
Нестандартный вариант шаблона:
Исходный текст на языке С#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 Restore() { originator.x = x; originator.y = y; originator.r = r; } } double x; double y; double r; public void 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 CircleMemento 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 Restore() { originator.x = x; originator.y = y; originator.w = w; originator.h = h; } } double x; double y; double w; double h; public void 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 void Draw(IEnumerable<IShape> shapes) { foreach(IShape shape in shapes) { shape.Draw(); } } public void MoveAndScale(IEnumerable<IShape> shapes) { foreach(IShape shape in shapes) { shape.Scale(10); shape.Move(3, 2); } } public IEnumerable<IMemento> SaveStates(IEnumerable<IShape> shapes) { List<IMemento> states = new List<IMemento>(); foreach(IShape shape in shapes) { states.Add(shape.GetState()); } } public void RestoreStates(IEnumerable<IMemento> states) { foreach(IMemento state in states) { state.Restore(); } } 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<IStates> 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); } }
Ссылки
- Паттерн Memento (хранитель) — назначение, описание, особенности и реализация на С++.
Шаблоны проектирования Основные Порождающие Структурные Поведенческие Интерпретатор • Итератор • Команда • Наблюдатель • Посетитель • Посредник • Состояние • Стратегия • Хранитель • Цепочка обязанностей • Шаблонный метод
Блокировка с двойной проверкой • Однопоточное выполнение • Планировщик Категория:- Шаблоны проектирования
Wikimedia Foundation. 2010.