Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Шаблоны и архитектура программ.doc
Скачиваний:
12
Добавлен:
04.05.2019
Размер:
558.08 Кб
Скачать

3.3. Структурные шаблоны: Декоратор, Заместитель, мост Декоратор (Decorator)

Шаблон Декоратор даёт способ для динамического добавления к объекту нового состояния и поведения. При этом исходный объект не знает о том, что он «декорируется». Ключевым моментом реализации шаблона является то, что класс-декоратор одновременно наследуется от декорируемого класса и агрегирует объект этого класса.

В качестве иллюстрации применения шаблона Декоратор рассмотрим программу для просмотра фотографий. Допустим, что существует возможность добавлять к каждой фотографии текстовые тэги, а также снабжать фотографии различными рамками. В этом примере фото с тэгом и фото с рамкой – это объекты-декораторы для исходно объекта-фотографии. Из примера также понятно, что декораторы должны быть независимы друг от друга.

UML-диаграмма, показывающая типичный дизайн шаблона Декоратор, представлена на рис. 3. На диаграмме и декоратор, и декорируемый объект реализуют общий интерфейс. Возможен вариант, когда вместо интерфейса используется общий класс-предок. Кроме этого, декоратор агрегирует декорируемый объект, добавляя к нему новое поведение.

Рис. 3. UML-диаграмма шаблона Декоратор.

При практической реализации шаблона декорируемый объект обычно передается как параметр конструктора декоратора и сохраняется во внутреннем поле. Методы декоратора могут использовать сохраненный объект при своей работе.

В качестве примера использования шаблона Декоратор рассмотрим код, имитирующий работу с фотографиями.

using System.Drawing;

public class Photo

{

public Image Image { get; set; }

public string Caption { get; set; }

public Photo()

{

Image = new Bitmap(100, 100);

}

public virtual void Draw(Graphics canvas)

{

canvas.DrawImage(Image, 0, 0);

}

}

public class TaggedPhoto : Photo

{

private readonly Photo photo;

private readonly string tag;

public TaggedPhoto(Photo source, string tag)

{

photo = source;

this.tag = tag;

}

public override void Draw(Graphics canvas)

{

photo.Draw(canvas);

canvas.DrawString(tag, new Font("Arial", 16),

new SolidBrush(Color.Black),

new PointF(20, 20));

}

}

public class DecoratorExample

{

private static void Main()

{

var photo = new Photo();

var taggedPhoto = new TaggedPhoto(photo, "the first tag");

var doubleTagged = new TaggedPhoto(taggedPhoto,

"the second tag");

}

}

Обратите внимание на то, что несколько декораторов независимо могут применяться к одному и тому же объекту, а также на то, что декоратор может декорировать объект, который уже декорирован.

Заместитель (Proxy)

Шаблон проектирования Заместитель применяется для контроля создания и доступа к объекту. Заместитель – это обычно небольшой открытый объект, «за спиной» которого находится сложный объект, выполняющий реальную работу и создающийся только при наступлении определённых условий.

Иллюстрацией шаблона Заместитель может служить работа с сайтом, который требует необязательной авторизации. Если пользователь не авторизовался, ему доступен сравнительно небольшой функционал. После авторизации (это условие создания сложного объекта) активируются дополнительные функции.

На рис 4. показана UML-диаграмма дизайна шаблона Заместитель. Для унификации доступа клиента к различным заместителям предполагается (хотя это и не обязательно), что все заместители реализуют общий интерфейс. Заместитель агрегирует сложный объект, контролируя доступ к нему. Клиент не обязательно имеет прямой доступ к замещаемому объекту.

Рис. 4. UML-диаграмма шаблона Заместитель.

Следующий пример кода показывает использование шаблона Заместитель. Первый заместитель управляет созданием замещаемого объекта – объект создаётся один раз при первой необходимости (такая разновидность шаблона носит название виртуальный заместитель). Второй заместитель показывает применение идей авторизованного доступа к функциям.

public interface ISubject

{

string Request();

}

internal class Subject

{

public string Request()

{

return "Subject Request";

}

}

// Виртуальный заместитель - создает реальный объект

// только при первом вызове его метода

public class VirtualProxy : ISubject

{

private Subject subject;

public string Request()

{

if (subject == null)

subject = new Subject();

return "Proxy: Call to " + subject.Request();

}

}

// Аутентификационный заместитель - для доступа нужен пароль

public class ProtectionProxy : ISubject

{

private Subject subject;

private const string Password = "Abracadabra";

public string Authenticate(string supplied)

{

if (supplied != Password)

return "Protection Proxy: No access";

subject = new Subject();

return "Protection Proxy: Authenticated";

}

public string Request()

{

if (subject == null)

{

return "Protection Proxy: Authenticate first";

}

return "Protection Proxy: Call to " + subject.Request();

}

}