Шаблоны проектирования

Шаблоны проектирования — это эффективные способы решения характерных задач проектирования, которые проявляются как при проектировании, так и при конструировании. Шаблон не является законченным образцом проекта, который может быть прямо преобразован в код, это описание или образец того, как решить определенную задачу и чтобы это решение можно было использовать в различных ситуациях. Объектно-ориентированные шаблоны определяют отношения и взаимодействия между классами или объектами без определения того, какие конечные классы или объекты приложения будут использоваться. Алгоритмы не рассматриваются как шаблоны, так как они решают задачи вычисления, а не проектирования.

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

  • 1. Имя. Сославшись на него, можно сразу описать проблему проектирования, ее решения и их последствия. Присваивание шаблонам имен позволяет проектировать на более высоком уровне абстракции. С помощью словаря шаблонов можно вести обсуждение с коллегами, упоминать шаблоны в документации, в тонкостях представлять дизайн системы. Нахождение хороших имен было одной из самых трудных задач при составлении каталога шаблонов.
  • 2. Задача. Описание того, когда следует применять шаблон. Необходимо сформулировать задачу и ее контекст. Может описываться конкретная проблема проектирования, например способ представления алгоритмов в виде объектов. Иногда отмечается, какие структуры классов или объектов свидетельствуют о негибком дизайне. Также может включаться перечень условий, при выполнении которых имеет смысл применять данный шаблон.
  • 3. Решение. Описание элементов дизайна, отношений между ними, функций каждого элемента. Не имеется в виду конкретный дизайн или реализация, поскольку шаблон применяется в самых разных ситуациях. Дается абстрактное описание задачи проектирования и того, как она может быть решена с помощью некоторого весьма обобщенного сочетания элементов (классов и объектов).
  • 4. Результаты. Они являются следствием применения шаблона и возможных компромиссов. Зачастую при описании проектных решений о последствиях не упоминают, а знать о них необходимо, чтобы можно было сделать выбор между различными вариантами данного шаблона, оценив их преимущества и недостатки.

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

Принципы работы с шаблонами проектирования. В распоряжение проектировщика предоставлен каталог более чем из 20 шаблонов, и поэтому трудно решить, какой шаблон лучше всего подходит для конкретной задачи.

В качестве примера можно привести следующие подходы к выбору подходящего шаблона: следует обдумать, как шаблоны решают проблему проектирования; проанализировать назначение шаблонов; изучить взаимосвязи шаблонов; проанализировать шаблоны со сходными целями; разобраться в причинах, вызывающих перепроектирование; посмотреть, что в вашем дизайне должно изменяться.

Работа с шаблонами состоит из следующих этапов.

  • 1. Прочитайте описание шаблона, чтобы получить о нем общее представление.
  • 2. Убедитесь, что понимаете упоминаемые в шаблоне классы и объекты и то, как они взаимодействуют друг с другом.
  • 3. Выберите для участников шаблона подходящие имена. Имена участников шаблона обычно слишком абстрактны, чтобы употреблять их непосредственно в коде. Тем не менее бывает полезно включить имя участника как имя в программе. Например, если вы пользуетесь шаблоном «стратегия в алгоритме размещения текста», то классы могли бы называться «SimpleLayoutStrategy» или «TeXLay-out Strategy».
  • 4. Определите классы, объявите их интерфейсы, установите отношения наследования и определите переменные экземпляра, которыми будут представлены данные объекты и ссылки на другие объекты. Выявите имеющиеся в вашем приложении классы, на которые шаблон оказывает влияние, и соответствующим образом модифицируйте их.
  • 5. Определите имена операций, встречающихся в шаблоне. Здесь, как и в предыдущем случае, имена обычно зависят от приложения. Руководствуйтесь теми функциями и взаимодействиями, которые ассоциированы с каждой операцией. Кроме того, будьте последовательны при выборе имен. Например, для обозначения фабричного метода можно было бы всюду использовать префикс «Create».
  • 6. Реализуйте операции, которые выполняют обязанности и отвечают за отношения, определенные в шаблоне.

Основные типы шаблонов проектирования. Приведем краткий обзор шаблонов проектирования (табл. 6.1). Для более детального ознакомления рекомендуется воспользоваться приведенным списком специализированной литературы. В конце будет показан пример реализации шаблона «Синглетон».

Порождающие шаблоны абстрагируют процесс создания экземпляров классов и помогают сделать систему независимой от способа создания, композиции и представления объектов. Шаблоны, порождающие классы, используют наследование, чтобы различать создаваемые экземпляры, а шаблоны, порождающие объекты, поручают создание другим объектам. Данный тип шаблонов важен, когда система больше зависит от композиции объектов, чем от наследования классов. Порождающие шаблоны обеспечивают большую гибкость при решении вопроса о том, что создается, кто это создает, как и когда. Можно собрать систему из «готовых» объектов с самой различной структурой и функциональностью статически (на этапе компиляции) или динамически (во время выполнения).

В качестве примера можно привести известный шаблон «Фабрика классов», который позволяет порождать объекты одинаковой структуры, но с различными свойствами, используя полиморфные классы (например, порождение одного и того же лабиринта с различными свойствами его элементов в зависимости от класса, который их создает).

Структурные шаблоны позволяют рассматривать вопрос, как из классов и объектов образуются более крупные структуры. Структурные шаблоны уровня класса используют наследование для составления композиций из интерфейсов и реализаций. В качестве

Шаблоны проектирования и их краткое описание

Порождающие

шаблоны

Абстрактная фабрика

Семейства порождаемых объектов

Одиночка

Единственный экземпляр класса

Прототип

Класс, из которого инстанцируется объект

Строитель

Способ создания составного объекта

Фабричный метод

Инстанцируемый подкласс объекта

Структурные

шаблоны

Адаптер

Интерфейс к объекту

Декоратор

Обязанности объекта без порождения подкласса

Заместитель

Способ доступа к объекту, его местоположение

Компоновщик

Структура и состав объекта

Мост

Реализация объекта

Приспособленец

Накладные расходы при хранении объектов

Фасад

Интерфейс к подсистеме

Шаблоны

поведения

Интерпретатор

Грамматика и интерпретация языка

Итератор

Способ обхода элементов агрегата

Команда

Время и способ выполнения запроса

Наблюдатель

Множество объектов, зависящих от другого объекта.

Способ, которым зависимые объекты поддерживают себя в актуальном состоянии

Посетитель

Операции, которые можно применить к объекту или объектам,

не меняя класса

Посредник

Объекты, взаимодействующие между собой, и способ их кооперации

Состояние

Состояние объекта

Стратегия

Алгоритм

Хранитель

Закрытая информация, хранящаяся вне объекта, и время ее сохранения

Цепочка обязанностей

Объект, выполняющий запрос

Шаблонный метод

Шаги алгоритма

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

Примером структурного шаблона уровня объектов является «Компоновщик». Он описывает построение иерархии классов для двух видов объектов: примитивных и составных. Последние позволяют создавать произвольно сложные структуры из примитивных и других составных объектов. В шаблоне «Заместитель» объект берет на себя функции другого объекта. У «Заместителя» есть много применений. Он может действовать как локальный представитель объекта, находящегося в удаленном адресном пространстве, или представлять большой объект, загружаемый по требованию, или ограничивать доступ к критически важному объекту. «Заместитель» вводит дополнительный косвенный уровень доступа к отдельным свойствам объекта, поэтому он может ограничивать, расширять или изменять эти свойства. Шаблон «Приспособленец» определяет структуру для совместного использования объектов. Он акцентирует внимание на эффективности использования памяти. В приложениях, где участвует много объектов, должны снижаться накладные расходы на их хранение (примером может служить программа, которая реализует текстовый редактор, поддерживающий произвольное форматирование любых участков текста). Шаблон «Фасад» представляет набор объектов и выполняет свои функции, перенаправляя сообщения объектам, которые он представляет. Шаблон «Мост» отделяет абстракцию объекта от его реализации, так что их можно изменять независимо.

Шаблоны поведения связаны с алгоритмами и распределением обязанностей между объектами, предназначены больше для проектирования типичных способов взаимодействия между объектами и классами. Шаблоны поведения характеризуют сложный поток управления, который трудно проследить во время выполнения программы. Внимание акцентировано не на потоке управления как таковом, а на связях между объектами. В шаблонах поведения уровня класса используется наследование, чтобы распределить поведение между разными классами, например шаблон, описывающий абстрактное определение алгоритма, где алгоритм определяется пошагово. На каждом шаге вызывается либо примитивная, либо абстрактная операция. Алгоритм усложняется, детализируется за счет подклассов, где определены абстрактные операции.

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

Пример 6.2. Рассмотрим пример реализации шаблона «Одиночка».

Название и классификация шаблона. «Одиночка» (Singleton) — шаблон, порождающий объекты.

Назначение. Гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа.

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

Как гарантировать, что у класса есть единственный экземпляр и чтобы этот экземпляр был легко доступен? Глобальная переменная дает доступ к объекту, но не запрещает создавать несколько экземпляров класса. Более удачное решение — сам класс контролирует то, что у него есть только один экземпляр, и может запретить создание дополнительных экземпляров, перехватывая запросы на создание новых объектов; он же способен предоставить доступ к своему экземпляру. Это и есть назначение шаблона «Одиночка».

Применимость. Используйте паттерн «Одиночка», когда должен быть ровно один экземпляр некоторого класса, доступный всем клиентам, или единственный экземпляр должен расширяться путем порождения подклассов и клиентам нужно иметь возможность работать

с расширенным экземпляром без модификации своего кода. Структура паттерна «Одиночка» представлена на рис. 6.8.

Singleton

Class

0 Fields

„Instance

0 Methods

Instance ^ Singleton

^....................-...................-..........................

J

Рис. 6.8. Структура шаблона «Одиночка»

Участники. «Singleton» — одиночка: определяет операцию Instance, которая позволяет клиентам получать доступ к единственному экземпляру (Instance — это статический метод класса); может нести ответственность за создание собственного уникального экземпляра.

Отношения. Клиенты получают доступ к экземпляру класса Singleton только через его операцию Instance.

Результаты. У шаблона «Одиночка» есть следующие достоинства.

  • 1) контролируемый доступ к единственному экземпляру. Поскольку класс «Singleton» инкапсулирует свой единственный экземпляр, он полностью контролирует то, как и когда клиенты получают доступ к нему;
  • 2) уменьшение числа имен. Шаблон позволяет избежать засорения пространства имен глобальными переменными, в которых хранятся уникальные экземпляры;
  • 3) допускает уточнение операций и представления. От класса «Singleton» можно порождать подклассы, а приложение легко настроить экземпляром расширенного класса. Можно конкретизировать приложение экземпляром того класса, который необходим во время выполнения;
  • 4) допускает переменное число экземпляров. Шаблон позволяет легко изменить решение и разрешить появление более одного экземпляра класса «Singleton». Можно применять один и тот же подход для управления числом экземпляров, используемых в приложении. Изменить нужно будет лишь операцию, дающую доступ к экземпляру класса «Singleton».

Реализация шаблона представлена листингом 6.17.

При использовании шаблона «Одиночка» предусмотрено следующее. Гарантирована единственность экземпляра. Шаблон устроен так, что больше одного экземпляра создать нельзя; для этого прячут операцию, создающую экземпляры, за статической функцией-членом или методом класса, которые гарантируют создание не более одного экземпляра. Данная операция имеет доступ к статическому полю класса, где хранится уникальный экземпляр, и гарантирует инициализацию переменной этим экземпляром перед возвратом ее клиенту. При таком подходе «Одиночка» будет создан и инициализирован перед первым использованием.

Листинг 6.17

Пример реализации шаблона «Одиночка»

class Singleton {

// Статический экземпляр класса private static Singleton „Instance = null;

// Конструктор private Singleton()

{

}

// Получение экземпляра класса public static Singleton Instance()

{

if („Instance == null)

„Instance = new SingletonO; return „Instance;

}

}

Клиенты осуществляют доступ к «Одиночке» исключительно через статический метод «Instance». Переменная _Instance инициализируется пустым значением Null, а статический метод «Instance» возвращает ее значение, инициализируя ее уникальным экземпляром, если в текущий момент оно равно Null. Метод «Instance» использует отложенную инициализацию: возвращаемое ей значение не создается и не хранится вплоть до момента первого обращения.

Необходимо отметить, что конструктор защищенный (private). Клиент, который попытается создать экземпляр класса «Singleton» непосредственно, получит ошибку на этапе компиляции. Это дает гарантию, что будет создан только один экземпляр. Поскольку переменная _Instance — указатель на объект класса «Singleton», то функция-член Instance может присвоить этой переменной указатель на любой подкласс данного класса.

 
< Пред   СОДЕРЖАНИЕ     След >