Хранитель | |
Memento | |
Тип: |
поведенческий |
---|---|
Описан в Design Patterns |
Да |
Хранитель (также известный как Memento, Token, Лексема) — поведенческий шаблон проектирования.
Позволяет, не нарушая инкапсуляцию, зафиксировать и сохранить внутреннее состояния объекта так, чтобы позднее восстановить его в этом состоянии.
Существует два возможных варианта реализации данного шаблона: классический, описанный в книге Design Patterns, и реже встречаемый нестандартный вариант.
Содержание |
Шаблон Хранитель используется, когда:
Классический вариант:
Нестандартный вариант:
Классический вариант: Шаблон Хранитель используется двумя объектами: "Создателем" (originator) и "Опекуном" (caretaker). "Создатель" - это объект, у которого есть внутреннее состояние. Объект "Опекун" может производить некоторые действия с "Создателем", но при этом необходимо иметь возможность откатить изменения. Для этого "Опекун" запрашивает у "Создателя" объект "Хранителя". Затем выполняет запланированное действие (или последовательность действий). Для выполнения отката "Создателя" к состоянию, которое предшествовало изменениям, "Опекун" возвращает объект "Хранителя" его "Создателю". "Хранитель" является непрозрачным (т.е. таким, который не может или не должен изменяться "Опекуном").
Нестандартный вариант: Отличие данного варианта от классического заключено в более жёстком ограничении на доступ "Опекуна" к внутреннему состоянию "Создателя". В классическом варианте у "Опекуна" есть потенциальная возможность получить доступ к внутренним данным "Создателя" через "Хранителя", изменить состояние и установить его обратно "Создателю". В данном варианте "Опекун" обладает возможностью лишь восстановить состояние "Хранителя", вызвав Restore. Кроме всего прочего, "Опекуну" не требуется владеть связью на "Хранителя", чтобы восстановить его состояние. Это позволяет сохранять и восстанавливать состояние сложных иерархических или сетевых структур (состояния объектов и всех связей между ними) путём сбора снимков всех зарегистрированных объектов системы.
Первый вариант шаблона:
//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
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); } }
Хранитель (шаблон проектирования).