Тема 16. Шаблоны проектирования
Для чего нужны шаблоны проектирования
Шаблоны проектирования относятся к архитектуре программ.
Шаблоны проектирования представляют собой некую архитектурную конструкцию, помогающую описать и решить определенную общую задачу проектирования. Они приобрели такую популярность потому, что разработка ПО ко времени их формализации уже была достаточно развита. Многие понимали, что не стоит изобретать велосипед, а использование паттернов часто бывает полезным как отдельному разработчику, так и целой команде.
Применение шаблонов проектирования связано и с определенными проблемами. В частности, распространено мнение, что только специалист, обладающий достаточно высокой квалификацией и понимающий, какие из паттернов ему нужны, сумеет правильно использовать их в своих программах. Кроме того, зачастую некоторые разработчики, изучившие лишь несколько шаблонов проектирования, начинают употреблять их повсюду, даже там, где они не слишком хорошо справляются с задачей и усложняют создаваемое ПО.
Шаблоны проектирования разделены на три основные группы:
- порождающие — призванные создавать объекты;
- структурные — меняющие структуру взаимодействия между классами;
- поведенческие — отвечающие за поведение объектов.
Несложно выделить и другие группы и даже антипаттерны, подсказывающие, как не надо разрабатывать ПО.
Интерфейс
Интерфейс описывает возможности классов и модулей. Он позволяет в большей степени разделить компоненты программы ( классы ) друг от друга.
Пример: Класс А использует класс В.


Без интерфейса – Классы связаны друг с другом. При изменении в 1 из классов нужно компилировать и перезапускать всю программу.
С интерфейсом - Классы взаимодействуют друг с другом через интерфейс. При изменении в 1 из классов нужно компилировать только этот класс. Также можно заменять классы без перезапуска всей программы.
Фабрика
Класс, который создает различные объекты по запросам.
Пример:
class CA{
...
}
class CB{
...
}
class ObjectFactory {
public object CreateObject( String oType )
{
object o = null;
if (oType == "CA")
return new CA();
if (oType == "CB")
return new CB();
return o;
}
}
Отложенная инициализация
Используется для инициирования объектов объектов не при старте программы, а когда он понадобится.
Преимущества: Быстрая загрузка программы. Лучшее управление памятью.
class CWorker{
...
}
class ObjectStorage {
// Объект vCWorker не инициирован
static CWorker vCWorker = null;
public static CWorker getCWorker ()
{
// Объект vCWorker инициируется только когда понадобится
// если еще не инициирован.
if (vCWorker == null)
vCWorker = new CWorker();
return vCWorker;
}
}
Одиночка
Используется когда только 1 объект может быть создан. Применяется напрмер при управлении большим объемом ресурсов или аппаратурой ( файловая система ).
class CMemoryStorage
{
static MemoryStream ms = null;
public CMemoryStorage() {
if ( ms != null )
throw new Exception("Нельзя создать еще объект.");
// 1 Gb
ms = new MemoryStream( 1024 * 1024 * 1024 );
}
public void Save( byte[] buffer ){
ms.Write( buffer, 0, buffer.Length );
}
}
Адаптер
Часто в новом программном проекте не удается повторно использовать уже существующий код. Например, имеющиеся классы могут обладать нужной функциональностью, но иметь при этом несовместимые интерфейсы. В таких случаях следует использовать паттерн Adapter (адаптер).
Паттерн Adapter, представляющий собой программную обертку над существующими классами, преобразует их интерфейсы к виду, пригодному для последующего использования.
Рассмотрим простой пример, когда следует применять паттерн Adapter. Пусть мы разрабатываем систему климат-контроля, предназначенной для автоматического поддержания температуры окружающего пространства в заданных пределах. Важным компонентом такой системы является температурный датчик, с помощью которого измеряют температуру окружающей среды для последующего анализа. Для этого датчика уже имеется готовое программное обеспечение от сторонних разработчиков, представляющее собой некоторый класс с соответствующим интерфейсом. Однако использовать этот класс непосредственно не удастся, так как показания датчика снимаются в градусах Фаренгейта. Нужен адаптер, преобразующий температуру в шкалу Цельсия.
// Уже существующий класс температурного датчика окружающей среды
class FahrenheitSensor
{
// Получить показания температуры в градусах Фаренгейта
public double getFahrenheitTemp() {
double t = 32.0;
// ... какой то код
return t;
}
}
class Sensor
{
public virtual float getTemperature();
};
class Adapter : Sensor
{
public
Adapter( FahrenheitSensor p ){
p_fsensor = p;
}
double getTemperature() {
return
( p_fsensor.getFahrenheitTemp() - 32.0 ) * 5.0 / 9.0;
}
private FahrenheitSensor p_fsensor = null;
}
Заместитель (Proxy)
Используется для замены одного обхекта другим. Работает аналогично Прокси-Серверу при доступе к интернету – Прокси-Сервер является заместителем прямого доступа к интернет.


Заместитель в архитектуре удаленного СОМ ( DCOM ) :


Дополнительная литература
http://habrahabr.ru/post/191934/ http://www.osp.ru/pcworld/2009/10/10685763/ http://andrey.moveax.ru/design-patterns/oop/ http://metanit.com/sharp/patterns/