- •А.А. Волосевич
- •3. Шаблоны и архитектура программ
- •3.1. Модульное тестирование
- •3.2. Шаблоны проектирования
- •3.3. Структурные шаблоны: Декоратор, Заместитель, мост Декоратор (Decorator)
- •Заместитель (Proxy)
- •Мост (Bridge)
- •3.4. Структурные шаблоны: компоновщик и приспособленец Компоновщик (Composite)
- •Приспособленец (Flyweight)
- •3.5. Структурные шаблоны: адаптер и фасад Адаптер (Adapter)
- •Фасад (Façade)
- •3.6. Порождающие шаблоны: прототип, фабричный метод, одиночка Прототип (Prototype)
- •Фабричный метод (Factory method)
- •Одиночка (Singleton)
- •3.7. Порождающие шаблоны: абстрактная фабрика и строитель Абстрактная фабрика (Abstract factory)
- •Строитель (Builder)
- •3.8. Шаблоны поведения: стратегия, состояние, шаблонный метод Стратегия (Strategy)
- •Состояние (State)
- •Шаблонный метод (Template method)
- •3.9. Шаблоны поведения: цепочка обязанностей и команда Цепочка обязанностей (Chain of responsibility)
- •Команда (Command)
- •3.10. Шаблоны поведения: итератор, посредник, наблюдатель Итератор (Iterator)
- •Посредник (Mediator)
- •Наблюдатель (Observer)
- •3.11. Шаблоны поведения: посетитель, интерпретатор, хранитель Посетитель (Visitor)
- •Интерпретатор (Interpreter)
- •Хранитель (Memento)
- •3.12. Некоторые неклассические шаблоны проектирования
- •Неизменный объект (Immutable object)
- •Пул объектов (Object pool)
- •Отложенная инициализация (Lazy initialization)
- •Нулевой объект (Null object)
- •3.13. Антипаттерны
- •3.14. Архитектура прогРаммного Обеспечения
- •«Клиент-сервер»
- •Архитектура, основанная на использовании компонентов
- •Многоуровневая архитектура
- •Шина сообщений
- •Выделенное представление
- •Объектно-ориентированная архитектура
- •Архитектура, ориентированная на сервисы
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();
}
}