- •А.А. Волосевич
- •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.12. Некоторые неклассические шаблоны проектирования
Набор шаблонов проектирования не ограничивается 23 шаблонами из книги «Design Patterns». Со временем список шаблонов расширялся. Некоторые популярные шаблоны представлены в данном параграфе.
Неизменный объект (Immutable object)
Шаблон Неизменяемый объект налагает запрет на изменение содержимого объекта после того, как объект был создан. Это повышает надежность кода при использовании объекта и уменьшает затраты на параллельный доступ к нему.
При практической реализации шаблона обычно придерживаются следующих правил:
начальной установкой полей объекта занимается конструктор и только он – не существует методов, которые бы изменяли внутренне состояние объекта;
состояние объекта доступно посредством открытых свойств «только для чтения»;
если метод получает информацию о новом состоянии объекта, то в теле метода для сохранения состояния создается новый экземпляр объекта.
В качестве иллюстрирующего кода для шаблона Неизменяемый объект приведем реализацию класса, описывающего координаты предмета.
public class Position
{
private int x;
private int y;
public int X { get { return x; } }
public int Y { get { return y; } }
public Position(int x, int y)
{
this.x = x;
this.y = y;
}
public Position Offset(int deltaX, int deltaY)
{
return new Position(x + deltaX, y + deltaY);
}
}
Заметим, что с точки зрения .NET Framework, неизменяемые объекты можно рассматривать как попытку смоделировать поведение типов значений при помощи ссылочных типов. Классическим примером неизменяемого объекта в .NET Framework является тип string. При практической реализации неизменяемого объекта могут дополнительно переопределяться операции проверки на равенство и получения копии.
Пул объектов (Object pool)
Пул объектов – это порождающий шаблон проектирования, который представляет собой набор инициализированных и готовых к использованию объектов. Когда системе требуется объект, он не создаётся, а берётся из пула. Когда объект больше не нужен, он не уничтожается, а возвращается в пул.
Пул объектов обычно применяется для повышения производительности, когда создание объекта в начале работы и уничтожение его в конце приводит к большим затратам. Особенно заметно повышение производительности, когда объекты часто создаются-уничтожаются, но одновременно существует лишь небольшое их число.
При практической реализации шаблона особого внимания заслуживает стратегия поведения в случае переполнения пула. В этом случае возможна одна из трёх стратегий: расширение пула, отказ в создании объекта и аварийный останов, ожидание освобождения одного из объектов в пуле.
Отложенная инициализация (Lazy initialization)
Отложенная (ленивая) инициализация – это приём в программировании, когда некоторая ресурсоёмкая операция (создание объекта, вычисление значения) выполняется непосредственно перед тем, как будет использован её результат. Таким образом, инициализация выполняется «по требованию», а не заблаговременно. Частный случай ленивой инициализации – создание объекта в момент обращения к нему – является одним из порождающих шаблонов проектирования. Как правило, он используется в сочетании с такими шаблонами как Фабричный метод, Одиночка и Заместитель.
Рассмотрим один из вариантов работы с отложенной инициализацией. Создадим обобщенный класс Lazy<T>, который будет представлять загрузку по требованию, а так же кэшировать результат вычислений для дальнейших обращений к ним.
public class Lazy<T>
{
private readonly Func<T> func;
private T result;
private bool hasValue;
public Lazy(Func<T> func)
{
this.func = func;
hasValue = false;
}
public T Value
{
get
{
if (!hasValue)
{
result = func();
hasValue = true;
}
return result;
}
}
}
Класс Lazy<T> имеет три поля:
func – делегат, инкапсулирующего передаваемый метод;
result – поле для хранения результата вычислений;
hasValue – флаг для обозначения, производились ли уже вычисления.
Описанный класс допускает следующий вариант использования:
Lazy<int> lazy = new Lazy<int>(() =>
{
Console.WriteLine("calculating...");
return 42;
});
Console.WriteLine("starting...");
Console.WriteLine("result = {0}", lazy.Value);
Console.WriteLine("result (again) = {0}", lazy.Value);