Обсуждение:Хранитель (шаблон проектирования)
Проект «Информационные технологии» (уровень IV)
Эта статья тематически связана с вики-проектом «Информационные технологии», цель которого — создание и улучшение статей по темам, связанным с информационными технологиями. Вы можете её отредактировать, а также присоединиться к проекту, принять участие в его обсуждении и поработать над требуемыми статьями. |
Другой вариант шаблона
[править код]Добрый день,
Во-первых, данная реализация шаблона нарушает инкапсуляцию, поскольку на самом деле мы отдаём наружу объект с публичными полями/методами доступа к ним, хранящими наши внутренние данные. Кроме того, кто-либо может вызвать конструктор Memento, передав ему свои данные, в итоге позволяется изменить состояние объекта Originator на совершенно произвольное. Это можно исправить в C++, сделав Originator friend-классом Memento, а конструктор закрыть и все методы доступа закрыть. В C# и Java такого способа, на сколько я себе представляю нет (разве только если сделать класс Originator внутренним для Memento(статическим внутренним для Java)).
Во-вторых, в случае, если в системе присутствует более одного объекта типа Originator, имеющих при этом разные интерфейсы, в данном варианте шаблона для каждого типа Originator придётся создать свой тип Memento и свой класс Caretaker:
public interface IShape
{
void Draw();
}
public class CircleMemento
{
public readonly double r;
public CircleMemento(double r)
{
this.r = r;
}
}
public class CircleOriginator : IShape
{
double r;
public void CircleOriginator(double r)
{
this.r = r;
}
public void Draw()
{
Console.WriteLine("Circle {0}", r);
}
public CircleMemento GetState()
{
return new CircleMemento(r);
}
public void SetState(CircleMemento m)
{
r = m.r;
}
}
public class RectMemento
{
public readonly double w;
public readonly double h;
public RectMemento(double w, double h)
{
this.w = w;
this.h = h;
}
}
public class RectOriginator : IShape
{
double w;
double h;
public RectOriginator(double w, double h)
{
this.w = w;
this.h = h;
}
public void Draw()
{
Console.WriteLine("Rectangle {0}x{1}", w, h);
}
public RectMemento GetState()
{
return new RectMemento(w, h);
}
public void SetState(RectMemento m)
{
w = m.w;
h = m.h;
}
}
Всё ухудшается, если требуется сохранить состояние сразу нескольких объектов системы. Логичным выглядело бы использовать шаблон компоновщик для снятия снимка сразу всей группы объектов, следовательно интерфейс у всех объектов Originator отличаться не должен:
public interface IOriginator
{
??? GetState();
void SetState(??? state);
}
Как видно, в этом случае требуется либо каким-либо образом привести Memento к одному интерфейсу. Плохой вариант совсем убрать указание типа.
public interface IOriginator
{
object GetState();
void SetState(object state);
}
но в этом случае внутри появляется приведение типов.
Поэтому я предлагаю иную структуру шаблона Caretaker/Memento. Пусть восстановление состояния объекта перейдёт к объекту Memento, тогда всё будет выглядеть несколько иначе:
public interface IShape
{
void Draw();
}
public interface IMemento
{
void RestoreState();
}
public interface IOriginator
{
IMemento GetState();
}
public class CircleOriginator : IShape, IOriginator
{
private class CircleMemento : IMemento
{
private readonly double r;
private readonly CircleOriginator originator;
public CircleMemento(CircleOriginator originator)
{
this.originator = originator;
r = originator.r;
}
public void Restore()
{
originator.r = r;
}
}
double r;
public void CircleOriginator(double r)
{
this.r = r;
}
public void Draw()
{
Console.WriteLine("Circle {0}", r);
}
public CircleMemento GetState()
{
return new CircleMemento(this);
}
}
public class RectOriginator : IShape, IOriginator
{
private class RectMemento : IMemento
{
private readonly double w;
private readonly double h;
private readonly RectOriginator originator;
public RectMemento(RectOriginator originator)
{
this.originator = originator;
w = originator.w;
h = originator.h;
}
public void Restore()
{
originator.w = w;
originator.h = h;
}
}
double w;
double h;
public void RectOriginator(double w, double h)
{
this.w = w;
this.h = h;
}
public void Draw()
{
Console.WriteLine("Rectangle {0}x{1}", w, h);
}
public IMemento GetState()
{
return new RectMemento(this);
}
}
Этот подход позволяет сохранять состояние очень сложных систем. В частности можно очень легко сохранить состояние составной фигуры, которая состоит из нескольких IShape (правда придётся слить воедино два интерфейса IShape и IOriginator). В этом случае запрос состояния составной фигуры вернёт Memento, состоящий из нескольких Memento внутренних объектов и восстанавливающий структуру базового объекта:
public interface IShape
{
void Draw();
void RestoreState();
}
public class CompoundOriginator : IShape
{
private class CompoundMemento : IMemento
{
private readonly CompoundMemento originator;
private readonly List<IShape> shapes;
private readonly List<IMemento> mementos;
public CompoundMemento(CompoundOriginator originator)
{
this.originator = originator;
shapes = new List<IShape>(originator.shapes);
mementos = new List<IMemento>();
foreach(var shape in shapes)
{
mementos.Add(shape.GetState());
}
}
public void Restore()
{
foreach(var memento in mementos)
{
memento.Restore();
}
originator.shapes = shapes;
}
}
List<IShape> shapes;
public void CompoundOriginator()
{
shapes = new List<IShape>();
}
public void Draw()
{
Console.WriteLine("Compound shape:");
foreach(var shape in shapes)
{
shape.Draw();
}
}
public IMemento GetState()
{
return new CompoundMemento(this);
}
}
Герыч 12:54, 21 апреля 2012 (UTC)