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

Оглавление (нажмите, чтобы открыть):

H Запоминаем шаблоны проектирования (design pattern) для собеседования и работы. Часть 1 в черновиках

.collapse»>Содержание

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

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

Осторожно: пятничный пост (да — в понедельник, но вот такая вот у меня пятницо 🙂 ), под катом смешные и нелепые картинки.

Паттерны проектирования (они же, Design pattern, они же, шаблоны проектирования, они же Жора, они же Гоша… ) — это набор «рецептов» по построению приложения. Причем «рецептов» достаточно общих.

Давайте рассмотрим их подробнее на аналогах из реального мира (чтобы упростить запоминания для ответов).

I. Порождающие шаблоны

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

Проще говоря, это шаблоны для создания других классов.

1) Абстрактная фабрика (Abstract factory).

Аналог — различные вендинговые машины

Представим, что нам требуется разработать сеть разных аппаратов по продаже шоколадок, кофе, печенек. Мы создаем проект некоторого абстрактного аппарата (который может выдавать шоколадок, кофе и печенки). Это будет абстрактная фабрика и абстрактные продукты.

На основе его делаем проекты большого количества реальных аппаратов, которые выдают конкретные виды продуктов. Это реальные фабрики унаследованные от абстрактной.

Они могут выглядеть так:

Но созданы на основе одного и того же проекта абстрактного аппарата.

Предоставляет интерфейс для создания семейств взаимосвязанных или взаимозависимых объектов, не специфицируя их конкретных классов.

Плюсы:

  1. изолирует конкретные классы;
  2. упрощает замену семейств продуктов;
  3. гарантирует сочетаемость продуктов.

Минусы:

  1. Добавить новый вид продукта достаточно сложно

public interface IButton <
void paint();
>

public interface IGUIFactory <
public IButton createButton();
>

public class WinFactory implements IGUIFactory <
Override
public IButton createButton() <
return new WinButton();
>
>

public class OSXFactory implements IGUIFactory <
Override
public IButton createButton() <
return new OSXButton();
>
>

public class WinButton implements IButton <
Override
public void paint() <
System.out.println(«WinButton»);
>
>

public class OSXButton implements IButton <
Override
public void paint() <
System.out.println(«OSXButton»);
>
>

public class Main <

2) Строитель (Builder).

Класс, который представляет собой интерфейс для создания сложного объекта. (с) вики

Аналог:

… заказ в пиццерии с возможностью выбора компонентов. Вы сами выбираете как создать вашу пиццу из заданных компонент. Сам билдер это повар, а результат его работы — полученный продукт (объект)

А если серьезнее:

3) Прототип (Prototype).

Определяет интерфейс создания объекта через клонирование другого объекта вместо создания через конструктор. (с) вики

Аналог:

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

4) Одиночка (Singleton)

Класс, который может иметь только один экземпляр. (с) вики

Аналоги:

… должность президента страны, должность генерального директора фирмы и т.п.

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

II. Структурные шаблоны

1) Адаптер (Adapter / Wrapper)

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

Аналог:

… синхронный переводчик. Позволяет понимать друг друга самым разным людям (и классам)

2) Компоновщик (Composite pattern)

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

Аналог в реальном мире:

… сержанты, офицеры в армии. Достаточно дать приказ только одному офицеру (сержанту), чтобы целая рота отправилась его выполнять. При этом сам офицер (сержант) такой же военный как и все остальные.

3) Шаблон фасад (Facade)

Структурный шаблон проектирования, позволяющий скрыть сложность системы путём сведения всех возможных внешних вызовов к одному объекту, делегирующему их соответствующим объектам системы. © Вики

Аналог:

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

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

Proxy — структурный шаблон проектирования, который предоставляет объект, который контролирует доступ к другому объекту, перехватывая все вызовы (выполняет функцию контейнера) © Вики

Аналог в реальном мире:

… секретарь у директора. Отфильтровывает неважные звонки и посетителей, если они не достаточно важны. При необходимости перенаправляя запросы реальному объекту (директору).

6) Приспособленец (Flyweight)

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

Аналог в реальном мире:

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

7) Декоратор (Decorator)

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

Аналог в реальном мире:

Шпион… то есть разведчик, например такой:

С одной стороны, на первый взгляд, обычный сотрудник, но на самом деле, с функцией хмм… логирования и нотификации важной информации.

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

Шаблоны проектирования по-человечески: структурные паттерны

Шаблоны проектирования — (паттерн, англ. design pattern) это многократно применяемая архитектурная конструкция, предоставляющая решение общей проблемы проектирования в рамках конкретного контекста и описывающая значимость этого решения. Паттерн не является законченным… … Википедия

Порождающие шаблоны проектирования — Порождающие шаблоны (англ. Creational patterns) шаблоны проектирования, которые абстрагируют процесс инстанцирования. Они позволяют сделать систему независимой от способа создания, композиции и представления объектов. Шаблон,… … Википедия

Поведенческие шаблоны проектирования — Поведенческие шаблоны (англ. behavioral patterns) шаблоны проектирования, определяющие алгоритмы и способы реализации взаимодействия различных объектов и классов. Использование В поведенческих шаблонах уровня класса используется… … Википедия

Паттерны проектирования — Шаблоны проектирования (паттерн, англ. design pattern) это многократно применяемая архитектурная конструкция, предоставляющая решение общей проблемы проектирования в рамках конкретного контекста и описывающая значимость этого решения. Паттерн не … Википедия

Шаблон проектирования — У этого термина существуют и другие значения, см. Паттерн. В разработке программного обеспечения, шаблон проектирования или паттерн (англ. design pattern) повторимая архитектурная конструкция, представляющая собой решение проблемы… … Википедия

Proxy (шаблон проектирования) — У этого термина существуют и другие значения, см. Proxy. Шаблон проектирования Заместитель Proxy Тип: структурный Описан в Design Patterns Да Шаблон Proxy (определяет объект заместитель англ. surrogate … Википедия

Адаптер (шаблон проектирования) — У этого термина существуют и другие значения, см. Адаптер. Шаблон проектирования Адаптер Adapter … Википедия

Заместитель (шаблон проектирования) — Шаблон Proxy (Заместитель) Шаблон проектирования. Предоставляет объект, контролирующий доступ, перехватывая все вызовы к нему. Содержание 1 Цель 1.1 Проблема 1.2 Решение 2 Плюсы 3 … Википедия

Шаблон Proxy (шаблон проектирования) — Шаблон Proxy (Заместитель) Шаблон проектирования. Предоставляет объект, контролирующий доступ, перехватывая все вызовы к нему. Содержание 1 Цель 1.1 Проблема 1.2 Решение 2 Плюсы 3 … Википедия

Декоратор (шаблон проектирования) — У этого термина существуют и другие значения, см. Декоратор. Шаблон проектирования Декоратор Decorator … Википедия

Основы паттернов проектирования

Введение в паттерны проектирования

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

Хотя идея паттернов как способ описания решения распространенных проблем в области проектирования появилась довольно давно, но их популярность стала расти во многом благодаря известной работе четырех авторов Эриха Гаммы, Ричарда Хелма, Ральфа Джонсона, Джона Влиссидеса, которая называлась «Design Patterns: Elements of Reusable Object-Oriented Software» (на русском языке известна как «Приемы объектно-ориентированного проектирования. Паттерны проектирования») и которая вышла в свет в 1994 году. А сам коллектив авторов нередко называют «Банда четырёх» или Gang of Four или сокращенно GoF. Данная книга по сути являлась первой масштабной попыткой описать распространенные способы проектирования программ. И со временем применение паттернов стало считаться хорошей практикой программирования.

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

Причем паттерны, как правило, не зависят от языка программирования. Их принципы применения будут аналогичны и в C#, и в Jave, и в других языках. Хотя в рамках данного руководства мы будем говорить о паттернах в контексте языка C#.

Также мышление паттернами упрощает групповую разработку программ. Зная применяемый паттерн проектирования и его основные принципы другому программисту будет проще понять его реализацию и использовать ее.

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

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

Порождающие паттерны

Порождающие паттерны — это паттерны, которые абстрагируют процесс инстанцирования или, иными словами, процесс порождения классов и объектов. Среди них выделяются следующие:

Абстрактная фабрика (Abstract Factory)

Фабричный метод (Factory Method)

Другая группа паттернов — структурные паттерны — рассматривает, как классы и объекты образуют более крупные структуры — более сложные по характеру классы и объекты. К таким шаблонам относятся:

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

Цепочка обязанностей (Chain of responsibility)

Шаблонный метод (Template method)

Существуют и другие классификации паттернов в зависимости от того, относится паттерн к классам или объектам.

Паттерны классов описывают отношения между классами посредством наследования. Отношения между классами определяются на стадии компиляции. К таким паттернам относятся:

Фабричный метод (Factory Method)

Шаблонный метод (Template Method)

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

Как вы освоили шаблоны проектирования?

1. Открываете папку с паттерном.
2. Читаете README.md с описание задачи.
3. Открываете exercise.php и пытаетесь решить задачу, применив соответствующий паттерн.
4. При необходимости вспоминаете теорию: github.com/domnikl/DesignPatternsPHP
5. Сверяетесь с решением в solution.php.

Когда начался бум и восторг вокруг концепции паттернов проектирования, выкрики «GoF рулит!» и так далее, я озадачился тем, чтобы понять, что за шум?

По своей сути — паттерны — это обычные шаблоны проектирования. Заимствовано у обычных архитекторов (те, которые зданиями занимаются). Суть проста. В работе архитектора есть задачи, которые удобно решать одним или несколькими проверенными способами.

По аналогии в проектировании софта имееются свои архитектурные вопросы вроде разбиения приложения на компоненты/модули, организации зависимостей между ними, распределение функциональных обязанностей и т.п. Как ловко подметили авторы книжки из этой банды четырех (The «Gang of Four») в нашей индустрии можно также выделить некоторе количество типовых шаблонов, проверенных на практике, чтобы тем самым не наступать на уже обойденные другими грабли.

Суть постижения паттернов заключается в том, чтобы осознать в каких ситуациях правильно использовать тот или иной шаблон проектирования и правильно его применить. Важно понимать при этом, что формула «чем больше паттернов я придумал засунуть с свое приложение — тем лучше» — неверная. Использовать их следует с умом и только там, где они действительно нужны. Кроме того, патерны устаревают, превращаются в анти-паттерны по мере развития технологий (которые в нашей области делают это более чем стремительно). Ну и, конечно, есть шаблоны общепринятые и есть те, которые успешно используются в узких кругах.
Тут тоже надо понимать, что это не догма какая-то — типа 10 священных паттернов проектирования 🙂

Чтобы понять, где они нужны — нужен опыт. То есть (я лично убежден), что учиться на ошибках других может только крайне ограниченное число людей. Все остальные обязаны набить шишки самостоятельно 🙂

К изучению паттернов я дам такие советы:

1) Прочтите пару книжек, чтобы понять, что это за зверь и с чем его едят. Можно взять одну из вариаций книжки GoF или какие-то производные для вашего стека разработки — познакомиться с основными популярными шаблонами. Сразу после этого я посоветовал бы прочесть книжку «Горький вкус Java» (Брюс Тейт) — она про анти-паттерны. Это чтобы понять обратную сторону их использования. Мне понравилась и уберегла думаю от многих проблем. То что на примере Java — неважно. Речь идет о шаблонах, так что представителям других стеков (к которым отношусь и я) будет просто понять все равно.

2) Постарайтесь осознать, доводилось ли вам сталкиваться в работе раньше с чем-то, что является или могло бы легко стать одним из шаблонов. Где получалось применить концепт верно, а где из-за этого только проблемы были.

3) В новых проектах, держите в голове полученные по шаблонам знания — вдруг пригодятся.

В конечном итоге, знаете ли вы паттерны, или нет — с опытом приходит понимание того, какая архитектура будет правильная, а какая — нет. Как сделать удобно, а как нет. И неважно, какими паттернами это обозвать.

Я даже пожалуй посоветовал бы подойти к освоению айтишной архитектурной мудрости с другой стороны — со стороны нефункциональных требований или так называемых «-ilities» — их много. Тут вот описаны 7 штук. А вообще их десятки.

Среди прочих — такие как maintainability (простая поддержка кода), scalability (масштабируемость), extensibility (расширяемость), availability (устойчивость ) и тп. Если, проектируя свое приложение, вы задумываетесь об этих «илитях» и стараетесь их обеспечить в необходимом проекту объеме, то, как правило, ваше приложение будет иметь отличную архитектуру. При этом шаблоны проектирования в ней появятся лаконично сами собой.

Поскольку идея использовать шаблоны — это попытка опытных программных инженеров дать десяток готовых рецептов менее опытным, чтобы пока они не научились варить «вкусную кашу», они не варили уж что-то совсем несъедобное. 🙂 Учитесь «готовить», разбирайтесь в -ilites 🙂 и все будет хорошо

Шпаргалка по шаблонам проектирования с примерами кода на С++

Описание паттернов, uml-схемы, плюс код на плюсах.

За небольшое время своей профессиональной деятельности я редко сознательно прибегал к использованию шаблонов проектирования. Решил себе сделать шпаргалку. Пост ничего принципиально нового не несет, он лишь способ для меня систематизации знаний. Скомпилировано на основе найденного в интернете(1, 2, 3, 4, 5, GOF , гугл и википедия). Пост постепенно будет дописываться и обновляться.

Содержание

Условные обозначения

  • — агрегация (aggregation) — описывает связь «часть»–«целое», в котором «часть» может существовать отдельно от «целого». Ромб указывается со стороны «целого».
  • композиция (composition) — подвид агрегации, в которой «части» не могут существовать отдельно от «целого».
  • — зависимость (dependency) — изменение в одной сущности (независимой) может влиять на состояние или поведение другой сущности (зависимой). Со стороны стрелки указывается независимая сущность.
  • — обобщение (generalization) — отношение наследования или реализации интерфейса. Со стороны стрелки находится суперкласс или интерфейс.

Базовые типовые решения

Шлюз (Gateway)

Это объект, инкапсулирующий доступ к внешней схеме или источнику данных. Смысл применения шлюза — обеспечить более удобный интерфейс для доступа к внешней системе. Весь специализированный код API помещается в класс-шлюз, интерфейс которого не отличается от интерфейса обычного объекта. Интерфейс шлюза может даже представлять собой точную копию инкапсулируемого интерфейса. Шлюз разрабатывается клиентом для использования внешнего приложения в отличие от интерфейса (Facade). В отличие от адаптера (Adapter) интерфейс шлюза не нужно подстраивать под какой-либо существующий интерфейс. И в отличие от медиатора (Mediator) шлюз разделяет только два объекта, причем источник данных не знает о существовании шлюза. Одно из главных назначений шлюза — обеспечить основу для реализации фиктивных служб (Service Stub).

Преобразователь (Mapper)

Это объект, устанавливающий взаимодействие между двумя независимыми объектами. Основное назначение преобразователя — отделить друг от друга различные части программной системы. Преобразователь представляет собой некий “изоляционный” слой, проложенный между двумя подсистемами. Он управляет взаимодействием подсистем, зачастую лишь перемещая данные из одного слоя в другой. Разделяемые подсистемы не зависят друг от друга, поэтому ни одна из них не может напрямую вызвать запуск преобразователя. Этот вызов обычно возлагают на третью подсистему, либо реализуют преобразователь в виде типового решения обозреватель (Observer). В отличие от медиатора (Mediator), объекты, разделенные преобразователем, не знают о наличии последнего.

Супертип слоя (Layer Supertype)

Это тип, выполняющий роль суперкласса для всех классов своего слоя. Супертип слоя используется тогда, когда все объекты соответствующего слоя имеют некоторые общие свойства или поведение. Чтобы избежать повторения, всё общее поведение можно вынести в супертип слоя. Если в рассматриваемом приложении находятся объекты нескольких различных типов, может понадобиться создать несколько супертипов слоя.

Отдельный интерфейс (Separated Interface)

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

Реестр (Registry)

Это “глобальный” (в действительности не обязательно таковой) объект, который используется другими объектами для поиска общих объектов или служб. В качестве интерфейса реестров рекомендуется применять статические методы. Обычно глобальный по отношению к процессу реестр реализуется в виде единственного элемента (Singleton). Рекомендуется использовать явные классы с явными методами, которые позволяют проследить, какие ключи применяются для поиска объекта. Явный класс позволяет сохранить типовую безопасность в статически типизированных языках, а также инкапсулировать структуру реестра, чтобы при последующем росте системы её можно было вынести в нужный класс или слой. Разные области видимости требуют различных реализаций, однако интерфейс при этом может быть общий.

Объект-значение (Value Object)

Также может иметься в виду объект переноса данных (Data Transfer Object)
Это небольшие простые объекты наподобие денежных значений или диапазонов дат, равенство которых не основано на равенстве идентификаторов. Применяется для моделирования тех сущностей, равенство которых определяется по значениям полей. Эти объекты делаются неизменяемыми, т.е. чтобы после создания объекта значения его полей не изменялись. Такие объекты можно передавать в виде значения, а не в виде ссылки на объект. Два объекта считаются равными если равны значения их полей. Обычно изменение полей объекта подразумевает полную замену старого объекта новым. Как правило для хранения объектов-значений применяют внедренные значения (Embedded Value) или крупные сериализованные объекты (Serialized LOB).

Деньги (Money)

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

Частный случай (Special Case)

Производный класс, описывающий поведение объекта в особых ситуациях. Суть заключается в возврате специального объекта с тем интерфейсом, который ожидает увидеть вызывающий метод. Следует применять для описания повторяющегося поведения, связанного с обработкой значений NULL или других особых ситуаций после выполнения соответствующих проверок на их наличие. Обычно, но не всегда, для реализации можно воспользоваться типовым решением приспособленец (Flyweight). Нередки ситуации, когда при переопределении методов в частном случае последний возвращает еще один частный случай.

Дополнительный модуль (Plugin)

Связывает классы во время настройки, а не во время компиляции приложения. Это типовое решение применяется тогда, когда одно и то же поведение может иметь несколько реализаций в зависимости от выбранной исполняющей среды. Также дополнительный модуль обеспечивает централизованную настройку приложения во время выполнения. Обычно определяется Отдельный интерфейс (Separated Interface), а затем пишется объект-фабрика (Factory Method) дополнительного модуля, который будет выполнять отображение интерфейса на необходимые реализации. Также дополнительный модуль комбинируется с типовым решением единственный элемент (Singleton). Применение дополнительного модуля наиболее предпочтительно в языках, поддерживающих отражение, поскольку объект-фабрика может создавать объекты реализаций без необходимости устанавливать зависимости во время компиляции.

Фиктивная служба (Service Stub)

Синонимы: объект-имитатор (Mock Object)

Устраняет зависимости приложения от труднодоступных или проблемных служб на время тестирования. Используется тогда, когда зависимость приложения от конкретной внешней службы значительно затрудняет процесс разработки и тестирования. Обычно устанавливают доступ к внешней службе с помощью шлюза (Gateway). Последний следует реализовать не как класс, а в виде отдельного интерфейса (Separated Interface), имеющего одну реализацию для обращения к реальной службе и, как минимум, еще одну реализацию, являющуюся фиктивной службой. Нужная реализация шлюза должна загружаться с помощью дополнительного модуля (Plugin). Сама реализация фиктивной службы должна быть достаточно простой, чтобы ускорить процесс разработки.

Множество записей (Record Set)

Представление табличных данных в оперативной памяти. Структура множества записей в точности копирует результат выполнения запроса к базе данных, однако может быть сгенерирована и использована другими частями системы. Обычно множество записей можно легко отсоединить от источника данных, что позволяет передавать множество записей по сети, не беспокоясь о наличии соединения с базой данных. Если множество записей легко поддается сериализации, оно может выступать в качестве объекта переноса данных (Data Transfer Object). Зачастую множество записей реализуют в виде единицы работы (Unit of Work), благодаря чему все изменения множества записей могут быть внесены в отсоединенном режиме и затем зафиксированы в базе данных. Источник данных может воспользоваться оптимистической автономной блокировкой (Optimistic Offline Lock), чтобы проверить параллельные транзакции на наличие конфликтов и в случае отсутствия последних записать все внесенные изменения в базу данных. Большинство реализаций множества записей используют неявный интерфейс (implicit interface), хотя он и обладает такими существенными недостатками как рассредоточение кода поиска и утрата компилятором информации о типах. Настоящая ценность множества записей раскрывается в тех средах, которые могут применять его в качестве стандартного способа манипулирования данными. При наличии подобных сред для организации логики домена следует применять модуль таблицы (Table Module).

Порождающие паттерны

  • Иногда порождающие паттерны конкурируют между собой: Есть случаи, когда либо Абстрактная Фабрика либо Прототип может использоваться с выгодой. В других случаях они дополняют друг друга: Абстрактная Фабрика может содержать множество Прототипов, из которых клонируются и возвращаются необходимые объекты, Строитель может использовать один из этих паттернов для постройки какого либо компонента. Абстрактная Фабрика, Строитель, Прототип могут использовать Синглтон в их реализации.
  • Абстрактная Фабрика часто реализуется при помощи Фабричного метода, так же это можно сделать применяя Прототип.
  • Абстрактная Фабрика может использоваться как альтернатива Фасаду для сокрытия платформенно специфичных классов.
  • Строитель фокусируется на построении комплексного объекта шаг за шагом. Абстрактная Фабрика акцентируется на семействе объектов (простых и сложных). Строитель возвращает результирующий объект на финальном шаге, в то время как Абстрактная Фабрика порождает объект сразу.
  • Строитель — для создания(порождения), Стратегия — для алгоритмов.
  • Строитель часто используют для порождения Компоновщика.
  • Фабричный метод обычно используется внутри Шаблонного метода.
  • Фабричный метод: порождение через наследование. Прототип: порождение через делегирование.
  • Часто проектировщик начинает с использования Фабричного метода(менее сложный, более настраиваемый, легко наследовать) и при усложнении проектируемой системы движется в сторону применения Абстрактной Фабрики, Прототипа или Строителя (более гибкие, более сложные).
  • Прототип не требует наследования, но нуждается в инициализации. Фабричный метод требует наследование, но не требует инициализацию.
  • Проекты, которые интенсивно используют Компоновщик и Декоратор часто могут извлечь выгоду также из Прототипа.

Фабричный метод (Factory Method)

Синонимы: Виртуальный конструктор (Virtual Constructor)
Определяет интерфейс для создания объектов, при этом выбранный класс инстанцируется подклассами, то есть оставляет подклассам решение о том, какой класс инстанцировать. Фабричный метод позволяет классу делегировать инстанцирование подклассам. Фабричные методы избавляют проектировщика встраивать в код зависящие от приложения классы.

Мастер Йода рекомендует:  Модуль CGI.pm

Применимость
Используется в случае:

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

Родственные паттерны
Абстрактная фабрика часто реализуется с помощью фабричных методов. Паттерн Фабричный метод часто вызывается внутри шаблонных методов.

Пример реализации
Паттерн реализуется через статический метод класса Stooge, возвращаемое знание типа Stooge. Но в отличие от обычного конструктора, возможно получить инстанс обьекта, который является наследником Stooge. Еще одно из преимуществ Фабричного метода — класс может многократно возвращать уже созданный инстанс объекта.

Абстрактная фабрика (Abstract Factory)

Синонимы: Инструментарий (Kit)
Предоставляет интерфейс для создания семейств, связанных между собой, или независимых объектов, конкретные классы которых неизвестны.

Применимость
Применять паттерн абстрактная фабрика необходимо, когда:

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

Родственные паттерны: Классы Abstract Factory часто реализуются фабричными методами (паттерн фабричный метод), но могут быть реализованы и с помощью паттерна прототип. Конкретная фабрика часто описывается паттерном одиночка.

Пример реализации
Обычно, пытаясь сохранить переносимость между несколькими платформами, требуется много предпроцессорных #ifdef. #else. #endif по всей простыне кода. Один из способов избежать этого — использовать паттерн “Абстрактная Фабрика”.

Одиночка (Singleton)

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

Применимость

  • должен быть ровно один экземпляр некоторого класса, легко доступный всем клиентам;
  • единственный экземпляр должен расширяться путем порождения подклассов, и клиентам нужно иметь возможность работать с расширенным экземпляром без модификации своего кода.

Пример реализации
Реализация не thread-safe.

Прототип (Prototype)

Описывает виды создаваемых объектов с помощью прототипа и создаёт новые объекты путём его копирования. У прототипа те же самые результаты, что у абстрактной фабрики (Abstract Factory) и строителя (Builder): он скрывает от клиента конкретные классы продуктов, уменьшая тем самым число известных клиенту имён. Кроме того все эти паттерны позволяют клиенту работать со специфичными для приложения классами без модификаций.

Применимость
Паттерн применяется, когда система не должна зависеть от того, как в ней создаются, компонуются и представляются продукты:

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

Отношения
Клиент обращается к прототипу, чтобы тот создал свою копию.

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

Пример реализации

Строитель (Builder)

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

Применимость

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

Отношения

  • клиент создает объект-распорядитель Director и конфигурирует его нужным объектом-строителем Builder;
  • распорядитель уведомляет строителя о том, что нужно построить очередную часть продукта;
  • строитель обрабатывает запросы распорядителя и добавляет новые части к продукту;
  • клиент забирает продукт у строителя.

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

Пример реализации
Сильная сторона Builder — это построение объекта шаг за шагом. Абстрактный класс декларирует шаги, конкретный наследник реализует эти шаги. В примере “distributed work packages” абстрагирован от платформенных особенностей.

Это означает, что для конкретной платформы механизм реализации файлов, очередей и путей определяется в конкретной производной класса. Объект “Reader” получает спецификацию из PersistenceAttribute для DistrWorkPackage и конфигурирует Builder , который был зарегистрирован клиентом, соответственно. После завершения клиент получает конечный результат от Builder’a.

Объектный пул (Object Pool)

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

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

Ловушки

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

Пример реализации

Структурные паттерны

  • Adapter makes things work after they’re designed; Bridge makes them work before they are.
  • Назначение моста — чтобы абстракция и реализация могли изменяться независимо. Предназначение адаптера — чтобы несвязанные классы могли работать вместе.
  • Адаптер обеспечивает различные интерфейсы к объекту, заместитель обеспечивает одинаковый интерфейс, декоратор расширяет интерфейс.
  • Адаптер изменяет интерфейс объекта, декоратор — расширяет. Поэтому он более прозрачный для клиента. Как следствие, декоратор поддерживает рекурсивную композицию, что невозможно с “чистым” адаптером.
  • Composite can be traversed with Iterator. Visitor can apply an operation over a Composite. Composite could use Chain of responsibility to let components access global properties through their parent. It could also use Decorator to override these properties on parts of the composition. It could use Observer to tie one object structure to another and State to let a component change its behavior as its state changes.
  • Composite can let you compose a Mediator out of smaller pieces through recursive composition.
  • Декоратор позволяет изменить внешний “облик”(интерфейсы) объекта. Стратегия позволяет изменить внутреннее устройство(реализацию) объекта.
  • Декоратор предназначен для добавления обязанностей объекту без его наследования. Фокус внимания компоновщика сосредоточен на представлении. Эти намерения различны, но являются взаимодополняющими. Следовательно, компоновщик и декоратор часто используются вместе.
  • Декоратор и Заместитель имеют разные цели, но похожие структуры. Both describe how to provide a level of indirection to another object, and the implementations keep a reference to the object to which they forward requests.
  • Facade defines a new interface, whereas Adapter reuses an old interface. Remember that Adapter makes two existing interfaces work together as opposed to defining an entirely new one.
  • Фасад зачастую синглтон, потому что только один обьект фасада нужен.
  • Mediator is similar to Facade in that it abstracts functionality of existing classes. Mediator abstracts/centralizes arbitrary communication between colleague objects, it routinely “adds value”, and it is known/referenced by the colleague objects. In contrast, Facade defines a simpler interface to a subsystem, it doesn’t add new functionality, and it is not known by the subsystem classes.
  • Whereas Flyweight shows how to make lots of little objects, Facade shows how to make a single object represent an entire subsystem.
  • Flyweight is often combined with Composite to implement shared leaf nodes.
  • Приспособленец explains when and how State objects can be shared.

Адаптер (Adapter)

Синонимы: Обертка (Wrapper).
Преобразует интерфейс класса в некоторый другой интерфейс, ожидаемый клиентами. Обеспечивает совместную работу классов, которая была бы невозможна без данного паттерна из-за несовместимости интерфейсов. Применимость

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

Отношения
Клиенты вызывают операции экземпляра адаптера Adapter. В свою очередь адаптер вызывает операции адаптируемого объекта или класса Adaptee, который и выполняет запрос.

Родственные паттерны
Структура паттерна Мост аналогична структуре адаптера, но у моста иное назначение. Он отделяет интерфейс от реализации, чтобы то и другое можно было изменять независимо. Адаптер же призван изменить интерфейс существующего объекта. Паттерн декоратор расширяет функциональность объекта, изменяя его интерфейс. Таким образом, декоратор более прозрачен для приложения, чем адаптер. Как следствие, декоратор поддерживает рекурсивную композицию, что для «чистых» адаптеров невозможно. Заместитель определяет представителя или суррогат другого объекта, но не изменяет его интерфейс.

Пример реализации
Реализация внешнего полиморфизма для трех несовместимых классов при помощи адаптера.

Более классический пример:
Интерфейс LegacyRectangle не совместимый с системой, в которой его хотят использовать. При помощи абстрактного класса специфицируется желаемый интерфейс. Класс-адаптер публично наследует абстрактный класс с желаемым интерфейсом и приватно LegacyRectangle . В своей реализации класс-адаптер “мапит” вызовы нового интерфейса к “старым”, уже реализованым интерфейсам LegacyRectangle .

Декоратор (Decorator)

Также может иметься в виду обертка (Wrapper). Динамически возлагает на объект новые функции. Декораторы применяются для расширения имеющейся функциональности и являются гибкой альтернативой порождению подклассов. Применимость

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

Отношения
Декоратор переадресует запросы объекту. Может выполнять и дополнительные операции до и после переадресации.

Родственные паттерны
Адаптер: если декоратор изменяет только обязанности объекта, но не его интерфейс, то адаптер придает объекту совершенно новый интерфейс. Компоновщик: декоратор можно считать вырожденным случаем составного объекта, у которого есть только один компонент. Однако декоратор добавляет новые обязанности, агрегирование объектов не является его целью. Стратегия: декоратор позволяет изменить внешний облик объекта, стратегия — его внутреннее содержание. Это два взаимодополняющих способа изменения объекта.

Пример реализации

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

Синонимы: суррогат (Surrogate)
Подменяет другой объект для контроля доступа к нему, то есть является суррогатом другого объекта. Заместитель применим во всех случаях, когда возникает необходимость сослаться на объект более изощренно, чем это возможно, если использовать простой указатель. С помощью этого паттерна при доступе к объекту вводится дополнительный уровень косвенности. Применимость
Паттерн заместитель применим во всех случаях, когда возникает необходимость сослаться на объект более изощренно, чем это возможно, если использовать простой указатель. Вот несколько типичных ситуаций, где заместитель оказывается полезным:

  • удаленный заместитель предоставляет локального представителя вместо объекта, находящегося в другом адресном пространстве;
  • виртуальный заместитель создает «тяжелые» объекты по требованию;
  • защищающий заместитель контролирует доступ к исходному объекту. Такие заместители полезны, когда для разных объектов определены различные права доступа;
  • “умная” ссылка — это замена обычного указателя. Она позволяет выполнить дополнительные действия при доступе к объекту. К типичным применениям такой ссылки можно отнести:
    • подсчет числа ссылок на реальный объект, с тем чтобы занимаемую им память можно было освободить автоматически, когда не останется ни одной ссылки (такие ссылки называют еще «умными» указателями);
    • загрузку объекта в память при первом обращении к нему;
    • проверку и установку блокировки на реальный объект при обращении к нему, чтобы никакой другой объект не смог в это время изменить его.

Отношения
Proxy при необходимости переадресует запросы объекту RealSubject. Детали зависят от вида заместителя.

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

Пример реализации
“->” и “.” операторы дают разный результат.

Компоновщик (Composite)

Группирует объекты в древовидные структуры для представления иерархии типа “часть-целое”. Позволяет клиентам работать с единичными объектами так же, как с группами объектов. Применимость

  • нужно представить иерархию объектов вида часть-целое;
  • необходимо, чтобы клиенты единообразно трактовали составные и индивидуальные объекты.

Отношения
Клиенты используют интерфейс класса Component для взаимодействия с объектами в составной структуре. Если получателем запроса является листовый объект Leaf, то он и обрабатывает запрос. Когда же получателем является составной объект Composite, то обычно он перенаправляет запрос своим потомкам, возможно, выполняя некоторые дополнительные операции до или после перенаправления.

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

Пример реализации

Мост (Bridge)

Синонимы: Описатель Тело (Handle Body)
Отделяет абстракцию от реализации, благодаря чему появляется возможность независимо изменять то и другое. Применимость

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

Отношения
Объект Abstraction перенаправляет своему объекту Implementor запросы клиента.

Пример реализации

Приспособленец (Flyweight)

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

  • в приложении используется большое число объектов;
  • из-за этого накладные расходы на хранение высоки;
  • большую часть состояния объектов можно вынести вовне;
  • многие группы объектов можно заменить относительно небольшим количеством разделяемых объектов, поскольку внешнее состояние вынесено;
  • приложение не зависит от идентичности объекта. Поскольку объекты-приспособленцы могут разделяться, то проверка на идентичность возвратит “истину” для концептуально различных объектов.

Отношения

  • состояние, необходимое приспособленцу для нормальной работы, можно охарактеризовать как внутреннее или внешнее. Первое хранится в самом объекте ConcreteFlyweight. Внешнее состояние хранится или вычисляется клиентами. Клиент передает его приспособленцу при вызове операций;
  • клиенты не должны создавать экземпляры класса ConcreteFlyweight напрямую, а могут получать их только от объекта FlyweightFactory. Это позволит гарантировать корректное разделение.

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

Пример реализации

Фасад (Facade)

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

  • необходимо предоставить простой интерфейс к сложной подсистеме. Часто подсистемы усложняются по мере развития. Применение большинства паттернов приводит к появлению меньших классов, но в большем количестве. Такую подсистему проще повторно использовать и настраивать под конкретные нужды, но вместе с тем применять подсистему без настройки становится труднее. Фасад предлагает некоторый вид системы по умолчанию, устраивающий большинство клиентов. И лишь те объекты, которым нужны более широкие возможности настройки, могут обратиться напрямую к тому, что находится за фасадом;
  • между клиентами и классами реализации абстракции существует много зависимостей. Фасад позволит отделить подсистему как от клиентов, так и от других подсистем, что, в свою очередь, способствует повышению степени независимости и переносимости;

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

Родственные паттерны
Паттерн абстрактная фабрика допустимо использовать вместе с фасадом, чтобы предоставить интерфейс для создания объектов подсистем способом, не зависимым от этих подсистем. Абстрактная фабрика может выступать и как альтернатива фасаду, чтобы скрыть платформенно-зависимые классы. Паттерн посредник аналогичен фасаду в том смысле, что абстрагирует функциональность существующих классов. Однако назначение посредника — абстрагировать произвольное взаимодействие между «сотрудничающими» объектами. Часто он централизует функциональность, не присущую ни одному из них. Коллеги посредника обмениваются информацией именно с ним, а не напрямую между собой. Напротив, фасад просто абстрагирует интерфейс объектов подсистемы, чтобы ими было проще пользоваться. Он не определяет новой функциональности, и классам подсистемы ничего неизвестно о его существовании. Обычно требуется только один фасад. Поэтому объекты фасадов часто бывают одиночками.

Пример реализации

Паттерны поведения

Интерпретатор (Interpreter)

Для заданного языка определяет представление его грамматики, а также интерпретатор предложений языка, использующий это представление. Пример реализации
Uses a class hierarchy to represent the grammar given below. When a roman numeral is provided, the class hierarchy validates and interprets the string. RNInterpreter “has” 4 sub-interpreters. Each sub-interpreter receives the “context” (remaining unparsed string and cumulative parsed value) and contributes its share to the processing. Sub-interpreters simply define the Template Methods declared in the base class RNInterpreter.

romanNumeral ::= thousands, hundreds, tens, ones ::= nine | four | nine ::= «CM» | «XC» | «IX» four ::= «CD» | «XL» | «IV» five ::= ‘D’ | ‘L’ | ‘V’ one ::= ‘M’ | ‘C’ | ‘X’ | ‘I’

Шаблонный метод (Template Method)

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

Итератор (Iterator)

Синонимы: Курсор (Cursor)
Дает возможность последовательно обойти все элементы составного объекта, не раскрывая его внутреннего представления. Пример реализации

Команда (Command)

Синонимы: Действие (Action), Сценарий транзакции (Transaction Script)
Инкапсулирует запрос в виде объекта, позволяя тем самым параметризовывать клиентов типом запроса, устанавливать очередность запросов, протоколировать их и поддерживать отмену выполнения операций.

Наблюдатель (Observer)

Синонимы: Подчиненные (Dependents), Издатель Подписчик (Publish Subscribe)
Определяет между объектами зависимость типа один-ко-многим, так что при изменении состояния одного объекта все зависящие от него получают извещение и автоматически обновляются. Шаблон позволяет изменять субъекты и наблюдатели независимо друг от друга. Субъекты разрешается повторно использовать без участия наблюдателей и наоборот. Это дает возможность добавлять новых наблюдателей без модификации субъекта или других наблюдателей.

Посетитель (Visitor)

Представляет операцию, которую надо выполнить над элементами объекта. Позволяет определить новую операцию, не меняя классы элементов, к которым он применяется.

Посредник (Mediator)

Определяет объект, в котором инкапсулировано знание о том, как взаимодействуют объекты из некоторого множества. Способствует уменьшению числа связей между объектами, позволяя им работать без явных ссылок друг на друга. Это, в свою очередь, дает возможность независимо изменять схему взаимодействия. Коллеги посылают запросы посреднику и получают запросы от него. Посредник реализует кооперативное поведение путем переадресации каждого запроса подходящему коллеге (или нескольким коллегам).

Состояние (State)

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

Стратегия (Strategy)

Синонимы: Политика (Policy)
Определяет семейство алгоритмов, инкапсулируя их все и позволяя подставлять один вместо другого. Можно менять алгоритм независимо от клиента, который им пользуется.

Хранитель (Memento)

Синонимы: Лексема (Token)
Позволяет, не нарушая инкапсуляции, получить и сохранить во внешней памяти внутреннее состояние объекта, чтобы позже объект можно было восстановить точно в таком же состоянии.

Цепочка обязанностей (Chain of Responsibility)

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

Представление бизнес-логики

Сценарий транзакции (Transaction Script)

Способ организации бизнес-логики по процедурам, каждая из которых обслуживает один запрос, инициализируемый слоем представления. Главным достоинством сценария транзакции является простота решения. Вам не приходится беспокоиться о наличии и вариантов функционирования других параллельных транзакций. Обычно сценарий транзакции размещается в классах, отличных от тех, которые относятся к слоям представления и источника данных. Может использоваться один класс для реализации нескольких сценариев транзакции или вариант, следующий схеме типового решения команда (Command) — собственный класс для каждого сценария транзакции. В последнем возможность манипулировать экземплярами сценариев как объектами в период выполнения помогает преодолевать проблемы потоков вычислений и облегчает изоляцию данных.

Модель предметной области (Domain Model)

Это объектная модель домена, охватывающая поведение (функции) и свойства (данные). Предусматривает создание сети взаимосвязанных объектов, каждый из которых представляет некую осмысленную сущность. Реализация модели предметной области означает пополнение приложения целым слоем объектов, описывающих различные стороны определенной области бизнеса. С моделью предметной области связано большое количество различных контекстов. Модели предметной области особенно хороши, когда существует множество схожих условий протекания процесса, которые могут быть отображены в структуре объектов как таковых. В этом случае сложность алгоритмов вычислений перемещается на уровень связей между объектами. Чем более близка логика, тем с большей вероятностью различные части кода системы должны пользоваться одними и теми же связями. Любому алгоритму вычислений соответствует определенная сеть объектов. Также назначение модели предметной области во многом состоит в сокрытии базы данных от верхних слоев системы.

Модуль таблицы (Table Module)

Объект, охватывающий логику обработки всех записей хранимой или виртуальной таблицы базы данных. Предусматривает создание по одному классу на каждую таблицу базы данных, и единственный экземпляр класса содержит всю логику обработки данных таблицы. Основное отличие модуля таблицы от модели предметной области (Domain Model) состоит в том, что если, например, приложение обслуживает множество заказов, в соответствии с моделью предметной области придется сконструировать по одному объекту на каждый заказ, а при использовании модуля таблицы понадобится всего один объект, представляющий одновременно все заказы. Сильная сторона решения модуль таблицы заключается в том, что оно позволяет сочетать данные и функции для их обработки и в то же время эффективно использовать ресурсы реляционной базы данных. На первый взгляд модуль таблицы во многом напоминает обычный объект, но отличается тем, что не содержит какого бы то ни было упоминания об идентификационном признаке объекта. Типовое решение модуль таблицы во многом основывается на табличной структуре данных и потому допускает очевидное применение в ситуациях, где доступ к информации обеспечивается при посредничестве множеств записей (Record Set). Структуре данных отводится центральная роль, так что методы обращения к данным в структуре должны быть прямолинейными и эффективными.

Слой служб (Service Layer)

Устанавливает множество доступных действий и координирует отклик приложения на каждое действие. Слой служб определяет границы приложения и множество операций, предоставляемых им для интерфейсных клиентских слоев кода. Он инкапсулирует бизнес-логику приложения, управляет транзакциями и координирует реакции на действия. Подобно сценарию транзакции (Transaction Script) и модели предметной области (Domain Model), слой служб представляет собой типовое решение по организации бизнес-логики. Слой служб предусматривает распределение “разной” логики по отдельным слоям, что обеспечивает традиционные преимущества расслоения, а также большую степень свободы применения классов домена в разных приложениях. Двумя базовыми вариантами реализации слоя служб являются создание интерфейса доступа к домену (Domain Facade) и конструирование сценария операции (Operation Script). Преимуществом использования слоя служб является возможность определения набора общих операций, доступных для применения многими категориями клиентов, и координация откликов приложения на выполнение каждой операции.

Архитектурные типовые решения источника данных

Шлюз таблицы данных (Table Data Gateway)

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

Шлюз записи данных (Row Data Gateway)

Объект, выполняющий роль шлюза (Gateway) к отдельной записи источника данных. Каждой строке таблицы базы данных соответствует свой экземпляр шлюза записи данных. Шлюз выступает в роли интерфейса к строке данных и прекрасно подходит для применения в сценариях транзакции (Transaction Script). Реализация шлюза записи данных включает в себя только логику доступа к базе данных и никакой логики домена. Добавление логики домена в шлюз записи данных превращает его в активную запись (Active Record).

Активная запись (Active Record)

Объект, выполняющий роль оболочки для строки таблицы или представления базы данных. Он инкапсулирует доступ к базе данных и добавляет к данным логику домена. В основе типового решения активная запись лежит модель предметной области (Domain Model). Каждая активная запись отвечает за сохранение и загрузку информации в базу данных, а также за логику домена, применяемую к данным. Это может быть вся бизнес-логика приложения. Впрочем, иногда некоторые фрагменты логики домена содержатся в сценариях транзакции (Transaction Script), а общие элементы кода, ориентированные на работу с данными, — в активной записи. Активная запись очень похожа на шлюз записи данных (Row Data Gateway). Принципиальное отличие между ними в том, что шлюз записи данных содержит только логику доступа к базе данных, в то время как активная запись содержит и логику доступа к данным, и логику домена. Активная запись хорошо подходит для реализации не слишком сложной логики домена, в частности операций создания, считывания, обновления и удаления. Кроме того, она прекрасно справляется с извлечением и проверкой на правильность отдельной записи.

Преобразователь данных (Data Mapper)

Это слой преобразователей (Mapper), который осуществляет передачу данных между объектами и базой данных, сохраняя последние независимыми друг от друга и от самого преобразователя. То есть это слой программного обеспечения, которое отделяет объекты, расположенные в оперативной памяти, от базы данных. В функции преобразователя данных входит передача данных между объектами и базой данных и изоляция их друг от друга. В результате этого объекты, расположенные в оперативной памяти, могут даже не “подозревать” о самом факте присутствия базы данных. Им не нужен SQL-интерфейс и тем более схема базы данных. (В свою очередь, схема базы данных никогда не “знает” об объектах, которые её используют.) Поскольку преобразователь данных является разновидностью преобразователя (Mapper), он полностью скрыт от уровня домена. В большинстве случаев преобразователь данных применяется для того, чтобы схема базы данных и объектная модель могли изменяться независимо друг от друга. Как правило, подобная необходимость возникает при использовании модели предметной области (Domain Model). Основным преимуществом преобразователя данных является возможность работы с моделью предметной области без учета структуры базы данных как в процессе проектирования, так и во время сборки и тестирования проекта. В этом случае объектам домена ничего не известно о структуре базы данных, поскольку все отображения выполняются преобразователями.

Мастер Йода рекомендует:  Поведенческие факторы – основа здорового ресурса

Объектно-реляционные типовые решения, предназначенные для моделирования поведения

Единица работы (Unit of Work)

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

Коллекция объектов (Identity Map)

Гарантирует, что каждый объект будет загружен из базы данных только один раз, сохраняя загруженный объект в специальной коллекции. При получении запроса просматривает коллекцию в поисках нужного объекта. Коллекция объектов может быть явной (explicit), кода доступ осуществляется посредством специализированных методов, соответствующих конкретному типу искомого объекта, или универсальной (generic), когда для доступа ко всем типам объектов используется общий метод. Как правило, коллекция объектов применяется для управления любыми объектами, которые были загружены из базы данных и затем подверглись изменениям. Основное назначение коллекции объектов — не допустить возникновения ситуации, когда два разных объекта приложения будут соответствовать одной и той же записи базы данных, поскольку изменение этих объектов может происходить несогласованно и, следовательно, вызывать трудности с отображением на базу данных. Еще одним преимуществом коллекции объектов является возможность использования ее в качестве кэша записей, считываемых из базы данных. Это избавляет от необходимости повторного обращения к базе данных, если снова понадобиться какой-нибудь объект.

Загрузка по требованию (Lazy Load)

Объект, который не содержит все требующиеся данные, однако может загрузить их в случае необходимости. Существует четыре основных способа реализации загрузки по требованию. Самый простой — инициализация по требованию (Lazy Initialization). Основная идея данного подхода заключается в том, что при каждой попытке доступа к полю выполняется проверка, не содержит ли оно значение NULL. Если поле содержит NULL, метод доступа загружает значение поля и лишь затем его возвращает. Если используется преобразователь данных (Data Mapper), то может понадобиться реализовать виртуальный прокси-объект (Virtual Proxy). Виртуальный прокси-объект имитирует объект, являющийся значением поля, однако в действительности ничего в себе не содержит. В этом случае загрузка реального объекта будет выполнена только тогда, когда будет вызван один из методов виртуального прокси-объекта. Диспетчер значений (Value Holder) — еще один способ реализации — это объект, который выполняет роль оболочки для какого-нибудь другого объекта. Чтобы добраться к значению базового объекта, необходимо обратиться за ним к диспетчеру значения. При первом обращении диспетчер значения извлекает необходимую информацию из базы данных. И наконец, фиктивный объект (Ghost) — это реальный объект с неполным состоянием. Когда подобный объект загружается из базы, он содержит только свой идентификатор. При первой же попытке доступа к одному из его полей объект загружает значения всех остальных полей. Помимо всего прочего, использование загрузки по требованию может повлечь за собой чрезмерное количество обращений к базе данных. Бессмысленно использовать загрузку по требованию, чтобы извлечь значения поля, хранящегося в той же строке, что и остальное содержимое объекта, даже если речь идет о полях большого размера, таких как крупный сериализованный объект (Serialized LOB) (тут я не совсем согласен с автором). Применение загрузки по требованию существенно усложняет приложение, поэтому прибегать к ней нужно только тогда, когда без нее действительно не обойтись.

Объектно-реляционные типовые решения, предназначенные для моделирования структуры

Поле идентификации (Identity Field)

Сохраняет идентификатор записи базы данных для поддержки соответствия между объектом приложения и строкой базы данных. Суть поля идентификации до смешного проста: требуется лишь сохранить первичный ключ таблицы реляционной базы данных в полях объекта, однако в реализации этого шаблона существует множество спорных моментов. Поле идентификации используется тогда, когда необходимо построить отображение между объектами, расположенными в оперативной памяти, и строками таблицы базы данных. Как правило, такая необходимость возникает при использовании модели предметной области (Domain Model) или шлюза записи данных (Row Data Gateway). Подобное отображение не нужно, если вы используете сценарий транзакции (Transaction Script), модуль таблицы (Table Module) или шлюз таблицы данных (Table Data Gateway).

Отображение внешних ключей (Foreign Key Mapping)

Отображает ассоциации между объектами на ссылки внешнего ключа между таблицами базы данных. Обычно реализуется посредством добавления обратного указателя на “объект-хозяин”, что приводит к появлению ссылки, аналогичной создаваемой внешним ключом. Это изменяет объектную модель, однако позволяет значительно упростить обновление данных путем простого обновления однозначных полей “с другой стороны” ссылки. Отображение внешних ключей может применяться для моделирования практически всех связей между классами. Наиболее распространенный случай, когда отображение внешних ключей применить нельзя, — это связи “многие ко многим”. Внешние ключи являются одномерными значениями, а из определения первой нормальной формы следует, что в одном поле нельзя хранить множественные значения внешних ключей. В этом случае вместо отображения внешних ключей необходимо воспользоваться отображением с помощью таблицы ассоциаций (Association Table Mapping).

Отображение с помощью таблицы ассоциаций (Association Table Mapping)

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

Отображение зависимых объектов (Dependent Mapping)

Передает некоторому классу полномочия по выполнению отображения для дочернего класса. Один класс (зависимый объект) передает другому классу (владельцу) все свои полномочия по взаимодействию с базой данных. При этом у каждого зависимого объекта должен быть один и только один владелец. Данный принцип проявляет себя в терминах классов, выполняющих отображение. В случае с активной записью (Active Record) и шлюзом записи данных (Row Data Gateway) зависимый класс не будет содержать никакого кода, касающегося выполнения отображения на базу данных; этот код будет реализован в классе владельце. В случае с преобразователем данных (Data Mapper) у зависимого класса не будет своего преобразователя; всё необходимое отображение будет выполняться преобразователем класса-владельца. И наконец, в случае шлюза таблицы данных (Table Data Gateway) зависимого класса не будет вообще; всю обработку зависимых данных будет осуществлять класс-владелец. Зависимый объект не имеет поля идентификации (Identity Field) и, следовательно, не заноситься в коллекцию объектов (Identity Map). Для зависимого объекта вообще не предусмотрено отдельных методов поиска, так как весь поиск выполняется объектом-владельцем. Отображение зависимых объектов может применяться только при соблюдении следующих условий: у каждого зависимого объекта должен быть строго один владелец; на зависимый объект может ссылаться только его владелец.

Внедренное значение (Embedded Value)

Отображает объект на несколько полей таблицы, соответствующей другому объекту. Обычно происходит отображение значения полей объекта на поля записи его владельца. Или другими словами, поля таблицы некоего объекта, как бы группируются в одном поле этого объекта, представляя собой подчиненный объект, содержащий значения полей объекта-владельца. Он может представлять собой, например, объект-значение (Value Object). Когда-объект владелец загружается из базы данных или сохраняется в ней, вместе с ним загружаются или сохраняются и зависимые объекты. Зависимые классы не имею собственных методов загрузки и сохранения, поскольку все эти операции выполняются их владельцем. Для большей наглядности внедренное значение можно рассматривать как частный случай отображения зависимых объектов (Dependent Mapping), где значения поля таблицы является отдельным зависимым объектом. Огромным преимуществом внедренного значения является возможность постановки SQL-запросов к полям зависимого объекта.

Сериализованный крупный объект (Serialized LOB)

Сохраняет граф объектов путем их сериализации в единый крупный объект и помещает его в поле базы данных. Это разновидность типового решения хранитель (Memento). Может представлять собой как крупный двоичный объект (BLOB), так и крупный символьный объект (CLOB). Первый сохранятся в виде двоичного формата данных, а второй представляет собой текстовую строку, например, в виде XML.

Наследование с одной таблицей (Single Table Inharitence)

Представляет иерархию наследования классов в виде одной таблицы, столбцы которой соответствуют всем полям классов, входящих в иерархию. Наследование с одной таблицей отображает все поля всех классов структуры на столбцы одной и той же таблицы. То есть структура наследования отображается на одну таблицу, которая содержит в себе все данные всех классов, входящих в иерархию наследования. Каждому классу (а точнее, его экземпляру) соответствует одна строка таблицы; при этом поля таблицы, которых нет в данном классе, остаются пустыми. Основное представление объектов, выполняющих отображение, соответствует общей схеме преобразователей наследования (Inheritance Mappers).

Наследование с таблицами для каждого класса (Class Table Inheritance)

Синонимы: отображение “корень-лист” (Root-Leaf Mapping)

Представляет иерархию наследования классов, используя по одной таблице для каждого класса. Идея наследование с таблицами для каждого класса проста и понятна: каждому классу модели предметной области соответствует своя таблица базы данных. Поля класса домена отображаются непосредственно на столбцы соответствующей таблицы. Как и в других схемах отображения иерархии наследования, в данном типовом решении применяется фундаментальный принцип преобразователей наследования (Inheritance Mappers).

Наследование с таблицами для каждого конкретного класса (Concrete Table Inheritance)

Представляет иерархию наследования классов, используя по одной таблице для каждого конкретного класса. При этом каждая таблица содержит столбцы, соответствующие полям конкретного класса и всех его “предков”, а потому поля суперкласса дублируются во всех таблицах его производных классов. Как и остальные схемы отображения иерархии наследования, данное типовое решение основано на фундаментальном принципе преобразователей наследования (Inheritance Mappers). Наследование с таблицами для каждого конкретного класса часто рассматривают как разновидность наследования с таблицами для каждого листа (Leaf Table Inheritance), когда создается по одной таблице для каждого листа иерархии наследования, а не для каждого конкретного класса.

Преобразователи наследования (Inheritance Mappers)

Структура, предназначенная для организации преобразователей, которые работают с иерархиями наследования. Минимизирует количество кода, необходимого для загрузки и сохранения содержимого базы данных, при отображении объектно-ориентированной иерархии наследования, благодаря точному распределению обязанностей по структуре преобразователей, реализующих как абстрактное так и конкретное поведение. Хотя детали поведения могут различаться в зависимости от выбранной схемы отображения (наследование с одной таблицей (Single Table Inheritence), наследование с таблицами для каждого класса (Class Table Inheritence), наследование с таблицами для каждого конкретного класса (Concrete Table Inheritence)), общая структура остается одной и той же.

Шаблоны, предназначенные для представления данных в Web

Модель-представление-контроллер (Model View Controller)

Распределяет обработку взаимодействия с пользовательским интерфейсом между тремя участниками. Модель — это объект, предоставляющий некоторую информацию о домене. Содержит в себе все данные и поведение и не связана с пользовательским интерфейсом. В наиболее “чистой” форме представляет собой объект модели предметной области (Domain Model). Представление отображает содержимое модели средствами графического интерфейса. Все изменения информации обрабатываются контроллером. Получая входные данные от пользователя, он выполняет операции над моделью и указывает представлению на необходимость соответствующего обновления. Данное типовое решение реализует два принципиальных типа разделения: отделение представления от модели, являющееся одним из фундаментальных принципов проектирования программного обеспечения, и отделение контроллера от представления. Последний тип разделения практически не играет такой важной роли в системах с толстым клиентом, однако в Web-интерфейсах отделение весьма полезно.

Контроллер страниц (Page Controller)

Объект, который обрабатывает запрос к Web-странице или выполнение конкретного действия на Web-сайте. Предполагает наличие отдельного контроллера для каждой логической страницы Web-сайта. Этим контроллером может быть сама страница или отдельный объект, соответствующий данной странице. То есть контроллер страниц может быть реализован в виде сценария (сценария CGI, сервлета и т.п.) или страницы сервера (ASP, PHP, JSP и т.п.). Контроллер страниц не обязательно должен представлять собой единственный класс, зато все классы контроллеров могут использовать одни и те же вспомогательные объекты. Контроллер страниц более прост в работе чем контроллер запросов (Front Controller) и представляет собой естественный механизм структуризации, при котором конкретные действия обрабатываются соответствующими страницами сервера или классами сценариев. Поэтому контроллер страниц хорошо применять для сайтов с достаточно простой логикой контроллера.

Контроллер запросов (Front Controller)

Контроллер, который обрабатывает все запросы к Web-сайту. Он объединяет все действия по обработке запросов в одном месте, распределяя их выполнение посредством единственного объекта обработчика. Как правило этот объект реализует общее поведение, которое может быть изменено во время выполнения с помощью декораторов (Decorator). Для выполнения конкретного запроса обработчик вызывает соответствующий объект команды (Command). Выбор команды может происходить статически или динамически.

Контроллер приложения (Application Controller)

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

Представление по шаблону (Template View)

Преобразует результат выполнения запрос в формат HTML путём внедрения маркеров в HTML-страницу. В основном выполняется вставка маркеров в текст готовой статической HTML-страницы, при вызове которой для обслуживания запроса эти маркеры будут преобразованы в вызовы функций, предоставляющих динамическую информацию. Подобная схема позволяет создавать статическую часть страницы с помощью обычных средств, например WYSIWYG-редакторов. Для избежания внедрения в страницу большого количества программной логики применяют вспомогательный объект (Helper Object). Он будет содержать в себе всю фактическую логику домена, а сама страница — только вызовы вспомогательного объекта. Это значительно упростит структуру страницы и максимально приблизит её к “чистой” форме представления по шаблону. Преимущество представления по шаблону перед представлением с преобразованием (Transform View) в том, что оно позволяет оформлять представление в соответствии со структурой страницы, а также воплотить в жизнь идею, когда дизайнер занимается проектированием страницы, а программист работает над вспомогательным объектом. Однако, реализуя представление в виде страницы сервера, последнюю весьма легко переполнить логикой. Также представление по шаблону сложнее тестировать, чем представлением с преобразованием (Transform View), так как большинство реализаций представления по шаблону ориентированы на использование в рамках Web-сервера.

Представление с преобразованием (Transform View)

Представление, которое поочередно обрабатывает элементы данных домена и преобразует их в код HTML. На вход подаются данные из модели, а на выходе принимается код HTML. При этом программа последовательно проходит по структуре данных домена и, обнаруживая новый фрагмент данных создает их описание в терминах HTML. Отличие представления с преобразованием от представления по шаблону (Template View) заключается в способе организации представления. Представление по шаблону организовано с учетом размещения на экране выходных данных. Представление с преобразованием ориентировано на использование отдельных преобразований для каждого вида входных данных. Преобразованиями управляет нечто наподобие простого цикла, который поочередно просматривает каждый входной элемент, подбирает для него подходящее преобразование и применяет это преобразование. Таким образом, правила представления с преобразованием могут быть организованы в любом порядке — на результат это не повлияет. Преобразования изначально направлены на визуализацию данных в формат HTML, что позволяет избежать внедрения в представление слишком большого количества логики. Представление с преобразованием легче тестировать, поскольку, в основном, его реализация не требует наличия функционирующего Web-сервера. Представления по шаблону (Template View) не выдерживает последних двух достоинств представления с преобразованием.

Двухэтапное представление (Two Step View)

Выполняет визуализацию данных домена в два этапа: вначале формирует некое подобие логической страницы, после чего преобразует логическую страницу в формат HTML. На первом этапе информация, полученная от модели, организуется в некую логическую структуру, которая описывает визуальные элементы будущего отображения, однако еще не содержит кода HTML. На втором этапе полученная логическая структура преобразуется в код HTML. Методы второго этапа “знают”, какие элементы есть в логической структуре и как визуализировать каждый из этих элементов. Таким образом, система с множеством экранов может быть трансформирована в код HTML путём единственного прохождения второго этапа, благодаря чему решение о варианте преобразования в HTML принимается в одном месте. Двухэтапное преобразование может быть построено как на основе представления с преобразованием (Transform View) так и на основе представления по шаблону (Template View). Главным преимуществом двухэтапного представления является возможность разбить преобразование данных на два этапа, что облегчает проведение глобальных изменений. Одноэтапный вариант представления, будь то представление по шаблону или представление с преобразованием, предусматривает по одному компоненту представления для каждой Web-страницы приложения. В двухэтапном представлении визуализация данных выполняется в два этапа, что требует по одному представлению первого этапа для каждой страницы приложения и единственное представление второго этапа для всего приложения. Последняя схема значительно облегчает изменение внешнего вида сайта на втором этапе, поскольку каждое такое изменение распространяется сразу на весь сайт. Таким образом, для приложений с несколькими вариантами внешнего вида используется меньше элементов регулировки отображения и чем больше интерфейсных экранов и вариантов внешнего вида есть у приложения, тем большим будет выигрыш.

ВИДЕОУРОК №2. Понятие паттерна

Доступ к полному курсу с учебными материалами и тестированием на 30 дней за 9.99 USD

Шаблоны и их использование в объективной реальности

Паттерны в спорте

Фигуры логики и модусы силлогизмов

Шаблоны в музыке

Порождающие шаблоны в музыке

Структурные шаблоны в музыке

Поведенческие шаблоны в музыке

Главная задача ООП

Шаблоны в программировании

Определение Паттерна

Формат описания паттернов проектирования

Каталог паттернов проектирования

Техника ООП

Фасад Подсистемы

Принципы организации каталога

Рекомендации по изучению паттернов

Разновидности шаблонов

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

1.1. Понятие паттерна проектирования

Определение

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

Паттерны (шаблоны) проектирования – это 23 примера, которые описывают:

  • правильные способы формирования внутреннего состояния (полей) и поведения (методов) объекта или класса;
  • правильные способы создания объекта (через вызов конструктора или другим способом);
  • правильные способы объединения объектов в группы;
  • правильные способы организации информационных потоков (вызовов методов и очередности вызовов) позволяющих наладить гармоничное взаимодействие между объектами и группами этих объектов в объектно-ориентированных системах.

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

Метафора

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

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

Основной шаг, Шассе, Кроссролл, Подсечка, Беговой шаг, Моухог, Чоктау и др.

Спираль в арабеске, спираль Шарлотты, Y -спираль, Fan -спираль, Спираль-Керриган и др.

Вращение стоя назад, Вращение-заклон, Вращение бильман, Вращение в ласточке (либела) и др.

Лутц, Тулуп, Флип, Аксель, Риттбергер, Сальхов, Сальто и др.

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

У каждого базового элемента имеется своя схема исполнения. Чтобы исполнить определенный элемент танца нужно выполнить ряд простейших действий (Разогнаться, повернуться, поднять ногу, расставить руки и пр.).

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

Рисунок 1. Схема исполнения прыжка «Axel»

При проектировании программных систем используется аналогичный подход. Программа строится с учетом наличия готовых подходов, которые представлены своими схемами – шаблонами (паттернами). В общем виде имеется 23 базовых шаблона проектирования программных систем, которые лежат в основе более высокоуровневых и модельно зависимых паттернов (например, паттерны проектирования корпоративных приложений, разработанные Мартином Фаулером, базируются на паттернах из каталога GoF ).

Между элементами, показанными на рисунках 2 и 3 можно провести соответствия.

Рисунок 2. Фрагмент танца составленный из двух независимых элементов (шаблонов)

Рисунок 3. Фрагмент программной системы, составленный из двух независмых элементов (шаблонов)

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

Рисунок 3. Соответствие

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

1.2. Формат описания паттернов проектирования

При рассмотрении паттернов проектирования используется единый формат описания. Описание каждого шаблона состоит из следующих разделов:

Название

Название паттерна (на Русском языке) отражающее его назначение.

Также известен как

Альтернативное название паттерна (если такое название имеется).

Классификация

Классификация паттернов производится:

  • По цели (порождающий, структурный или поведенческий)
  • По применимости (к объектам и/или к классам)

Частота использования

Низкая — 1 2 3 4 5

Ниже средней — 1 2 3 4 5

Средняя — 1 2 3 4 5

Выше средней — 1 2 3 4 5

Высокая — 1 2 3 4 5

Назначение

Краткое описание назначения паттерна и задачи проектирования, решаемые с его использованием.

Введение

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

Структура паттерна на языке UML

Графическое представление паттерна с использованием диаграмм классов языка UML. На диаграммах показаны основные участники (классы) и связи отношений между участниками.

Структура паттерна на языке C#

Программная реализация паттерна с использованием языка C #.

Участники

Имена участников (классы которые входят в состав паттерна) и описание их назначения.

Отношения между участниками

Описание отношений (взаимодействий) между участниками (классами и/или объектами).

Мотивация

Определение потребности в использовании паттерна. Рассмотрение способов применения паттерна.

Применимость паттерна

Рекомендации по применению паттерна.

Результаты

Особенности и варианты использования паттерна. Результаты применения.

Реализация

Описание вариантов и способов реализации паттерна.

Пример кода

Дополнительные примеры, иллюстрирующие использование паттерна.

Известные применения паттерна в .Net

Использование паттерна в . Net Framework и/или его выражение в языке C #.

1.3. Каталог паттернов проектирования

Каталог состоит из 23 паттернов. Все паттерны разделены на три группы:

Порождающие

  1. Abstract Factory (Абстрактная Фабрика)
  2. Builder (Строитель)
  3. Factory Method ( Фабричный Метод )
  4. Prototype ( Прототип )
  5. Singleton (Одиночка)

Структурные

  1. Adapter (Адаптер)
  2. Bridge ( Мост )
  3. Composite ( Компоновщик )
  4. Decorator ( Декоратор )
  5. Facade (Фасад)
  6. Flyweight (Приспособленец)
  7. Proxy (Заместитель)

Поведенческие

  1. Chain of Responsibility ( Цепочка Обязанностей )
  2. Command (Команда)
  3. Interpreter (Интерпретатор)
  4. Iterator (Итератор)
  5. Mediator (Посредник)
  6. Memento (Хранитель)
  7. Observer (Наблюдатель)
  8. State (Состояние)
  9. Strategy ( Стратегия )
  10. Template Method ( Шаблонный Метод )
  11. Visitor (Посетитель)

1.4. Техники ООП

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

Фабрика — Продукт

Техника использования объекта-фабрики для порождения объектов-продуктов, была положена в основу всех порождающих паттернов. Методы, принадлежащие объекту-фабрике, которые порождают и возвращают объекты-продукты, принято называть фабричными-методами (или виртуальными конструкторами).

См. пример к главе: \RulesOOP (Creating)

На диаграмме последовательностей можно отследить работу фабричной техники.

Рисунок 4. Диаграмма последовательностей фабричной техники

Фасад — Подсистема

Техника использования объекта-фасада, скрывающего за собой работу с неким подмножеством объектов, легла в основу структурных паттернов.

См. пример к главе: \RulesOOP ( Structural)

На диаграмме последовательностей можно отследить работу фасадной техники.

Рисунок 5. Диаграмма последовательностей фасадной техники

Диспетчеризация

Техника использования диспетчеризации имеет две формы: «Цепочка объектов» и «Издатель-Подписчик». Эти техники были положены в основу поведенческих паттернов.

Цепочка объектов

При использовании техники «Цепочка объектов» — объекты связываются в цепочку, вдоль которой происходит серия вызовов методов (посылка сообщений).

См. пример к главе: \ RulesOOP ( Chain )

На диаграмме последовательностей можно отследить работу техники «Цепочка объектов».

Рисунок 6. Диаграмма последовательностей техники «Цепочка объектов».

Издатель-Подписчик

При использовании техники «Издатель-Подписчик» — объект-издатель вызывает метод на объекте-подписчике, а объект-подписчик после этого вызывает метод на объекте-издателе. Таким образом объект-издатель уведомляет объекта-подписчика о наступлении некоторого события.

См. пример к главе: \ RulesOOP ( Behavioral )

На диаграмме последовательностей можно отследить работу техники «Издатель-Подписчик».

Рисунок 7. Диаграмма последовательностей техники «Издатель-подписчик»

1.5. Принципы организации каталога

Все 23 паттерна классифицируются по двум критериям – цель и применимость (уровень).

Таблица 1. Принципы организации каталога паттернов.

Цель паттерна

Цель паттерна – показывает его назначение.

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

Уровень паттерна — показывает область применения паттерна: к классам или к объектам.

  • Паттерны уровня классов описывают отношения между классами и их подклассами. Такие отношения выражаются при помощи статических связей отношений – наследования и реализации.
  • Паттерны уровня объектов описывают взаимодействия между объектами. Такие отношения выражаются при помощи динамических связей отношений – ассоциации, агрегации и композиции.

1.6. Рекомендации по изучению паттернов

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

Каждая из двух категорий разработчиков делится на три подкатегории: практики (большинство), теоретики (меньшинство) и те, кто гармонично сочетает теорию с практикой. Паттерны не терпят крайностей, для достижения максимального эффекта от обучения рекомендуется знакомство с теорией, изложенной в книге параллельно с рассмотрением прилагаемых примеров.

Начинающим программистам понадобится немного больше времени для изучения и хорошего понимания паттернов. Им рекомендуется сначала приступить к ознакомлению с диаграммой классов и сравнению классов и связей отношений, изображенных на диаграмме с реализацией паттерна в примере на языке C#. Такой подход позволит закрепить понимание простейших техник ООП, которые используются при построении паттерна. Для достижения большего эффекта есть смысл при изучении кода паттерна параллельно рисовать диаграмму объектов (овалы — символизирующие объекты в памяти и стрелки – показывающие как одни объекты ссылаются на другие).

Мастер Йода рекомендует:  Сколько можно заработать на партнёрке

Начать рассмотрение паттерна лучше всего с тела метода Main , сперва ознакомившись с интерфейсом взаимодействия используемых объектов. Далее есть смысл переходить к знакомству с классами используемых объектов.

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

Важно понимать, что паттерн — это формула, а пример использования паттерна — это пример применения этой формулы. Формула – первична, ее применение – вторично. Для создателей каталога паттернов формула была вторична. В основу этого каталога была положена докторская диссертация Эриха Гаммы – а это значит сначала исследования в области построения объектно-ориентированных систем, затем формализация результатов исследований и представление их в виде 23 паттернов (формул). Разработчикам исследовать ничего не нужно, им не нужно порождать новых знаний и делать открытий, им просто требуется использовать готовые паттерны (формулы) в повседневной работе для решения проектных задач.

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

1.7. Рекомендации по применению паттернов

Использовать паттерны просто для тех, кто знает наизусть все 23 паттерна (всех участников и связи отношений между ними). 23 паттерна – это «таблица умножения» проектировщика. Как трудно производить расчеты без знания таблицы умножения, также трудно проектировать приложения без знания паттернов.

Нет надобности искать в каком месте и когда применить тот или иной паттерн. Выбор паттерна – это выбор способа решения задачи. Нет задачи – нет и решения. Поставленная задача – причина. Паттерн — путь к следствию. Решенная задача – следствие.

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

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

[!] Паттерны проектирования — справочник, список — (шаблоны проектирования) — Design Patterns

Primary tabs

Forums:

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

На этой странице будет краткий справочник по паттернам (шаблонам) проектирования (их свойствам)

Порождающие паттерны

Структурные паттерны

Поведенческие паттерны

В процессе создания:

Возможно. в процессе чтения вы сравниваете паттерны и у вас возникают вопросы — это естественнно — попробуйте найти ответ на интересующий вас вопрос здесь

Паттерны — примеры из жизни

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

Порождающие шаблоны проектирования (Creational)

Пример из жизни: строительная бригада, которая умеет строить Дом с Садом, или Дом с бассейном, или Дом с гаражом и бассейном

Класс, последовательный вызов функций которого, создают объект только одного вида, но часто с разным содержимым: смотри пример из жизни. Хорошей реализацией считается, когда в классе-строителе есть функция финализации (finalize), вызов которой, помечает данный объект свойством законченности, т.е. объект становится более неизменяемым.

Пример из жизни: процесс деления клеток

Пример в программировании: функция, которая умеет создавать копию объекта в котором она находится или копию переданного в нее объекта

Структурные шаблоны проектирования (Structural)

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

Декоратор

Также известен как: Обёртка, Decorator

Пример из жизни

Декоратор — человек, который принимает заказы только на оформление чего-либо, например: театр, магазин, картина.

В программном смысле

Класс, расширяющий функциональность другого класса c использованием наследования.

Фасад

Также известен как: Facade

Пример из жизни

Завод по изготовлению какой-либо продукции имеет офис, в котором находится персонал принимающий заявки от клиентов и отдающий распоряжения на изготовление продукции.

В программном смысле

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

Важный момент

Часто целью данного паттерна является соблюдение закона Деметры .

Фабрика vs Фасад

Фасад не создает новые объекты.

Также известен как: Gateway

Пример из жизни

Любой компьютер при подключении к интернету, должен себя идентифицировать у интернет-провайдера, обычно, компьютер показывает Вам форму ввода логина/пароля (форма в этом случае является реализацией паттерна Фасад), которые после ввода отправляется на шлюз интернет-провайдера.

Фасад vs Шлюз

Фасад реализуется стороной которая обращается куда-либо, а Шлюз реализуется стороной к которой обращаются.

Компоновщик

Также известен как: Дерево, Composite

Пример из жизни

Компоновщик (от лат. componere «складывать») — человек, который может сложить предметы в коробку, и предоставить информацию о том, сколько в коробке предметов, какова их общая стоимость, и т.п. обобщенную информацию. Так же, компоновщики часто собирают что-то большое из чего-то маленького, например автомобиль из деталей, деталь автомобиля из более мелких деталей.

В программном смысле

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

Адаптер

Также известен как: Adapter / Wrapper

Пример из жизни

Переходник с одного типа розетки на другой тип розетки, и есть Адаптер.

В программном смысле

Объект реализующий интерфейсы двух других объектов, которым нужно взаимодействие друг с другом.

Также известен как: Bridge

Пример из жизни

Есть 2 города разделяемые рекой, для взаимодействия между ними строят мост, города развиваются каждый по-своему, но мост не меняется, городам приходится учитывать строение моста (города подстраиваются под мост, а не мост под города).

В программном смысле

Чаще всего, мост — это класс с интерфейсом, который должны реализовать оба объекта (которым нужно взаимодействие между собой)

Адаптер vs Мост

Паттерн Мост очень похож на Адаптер, но Адаптер адаптируется под обе стороны, а Мост говорит, чтобы обе стороны подстраивались под него

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

Альтернативные названия: Flyweight

Пример из жизни

Человеку приходится адаптироваться к погодным условиям, например зимой одевать теплые вещи.

В программном смысле

Класс реализующий существующий интерфейс, для взаимодействия.

Важный момент

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

Адаптер vs Приспособленец

Приспособленец это конечная вещь, а Адаптер, это промежуточная вещь

Заместитель

Также известен как: Proxy

Пример из жизни

Заместитель руководителя имеет те же полномочия что и руководитель (в момент отсутствия руководителя).

В программном смысле

Объект, который перехватывает вызовы к оригинальному объекту.

Поведенческие шаблоны проектирования (Behavioral)

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

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

Также известен как: CoR, Chain of Command, Chain of Responsibility

Пример из жизни

Вы купили новый компьютер, но он не включается — Вы звоните в компанию-производитель, и начинается цепочка получения ответа:

  1. с Вами говорит робот, и если он не смог дать ответ, то переключает Ваш звонок далее
  2. с Вами говорит оператора поддержки клиентов, и если он не смог дать ответ, то переключает Ваш звонок далее
  3. с Вами говорит инженер и т.д.

В программном смысле

Цепочка образуется из звеньев цепи — объектов, и имеет следующие особенности:

  1. каждое из звеньев цепи имеет ссылку на следующее звено в цепи (одно звено знает только про одно звено, кроме последнего звена, которое не знает ничего о другом звене), таким образом, при получении запроса звено может что-то сделать с запросом, и обязано передать запрос следующему звену в цепочке.
  2. если звену не интересен запрос, звено не обрабатывает запрос и просто передает его следующему звену
  3. длина цепочки не имеет никакого значения
  4. передавая запрос в первое звено, Вы можете быть уверены, что все звенья в цепи смогут его обработать
  5. есть и подход, когда звено не обязательно должно передавать запрос дальше

Команда

Также известен как: Command

Пример из жизни

Генерал отдает Полковнику команду «наступление на противника», каждый из Полковников отдает свою команду, например «наступление по левому флангу». Следовательно, Генералу важно, чтобы каждый Полковник правильно понял команду, Полковнику важно, чтобы каждый Рядовой правильно понял команду. Чтобы все правильно понимали команду, нужно всех обучить всем известным командам.

Важно: паттерн подразумевает простую возможность достижения чего-то более сложного

В программном смысле

Объект X реализованный по интерфейсу Command, о котором знает объект A, чтобы объект A мог обращаться к объекту X по заранее согласованным функциям интерфейса.

Фасад vs Команда

Паттерн «Фасад» не обязывает реализовывать какой-либо интерфейс, а паттерн «Команда» — обязывает (Генерал знает команду, благодаря которой Полковник сделает все правильно).

Итератор

Также известен как: Iterator

Пример из жизни

Путешественник, который рисует на карте точки и стрелки, которыми любой желающий может воспользоваться, чтобы пройти незабываемое путешествие.

В программном смысле

Идея паттерна Итератор состоит в том, чтобы вынести поведение обхода коллекции из самой коллекции в отдельный класс. Отсюда, имеет 2 вида итераторов:

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

Стратегия

Также известен как: Strategy

Пример из жизни

Вам нужно добраться до аэропорта. Можно доехать на автобусе, такси или велосипеде. Здесь вид транспорта является стратегией. Вы выбираете конкретную стратегию в зависимости от контекста — наличия денег или времени до отлёта.

В программном смысле

Класс Context имеет функцию поиска, которая пробегается по известным ей классам (эти классы обязаны имплементировать интерфейс Strategy требующей создать функцию, которую будет вызывать класс Context).

Посредник

Также известен как: Intermediary, Controller, Mediator

Пример из жизни

Пилоты самолётов не общаются напрямую с другими пилотами. Вместо этого они связываются с диспетчером, который координирует действия самолётов.

В программном смысле

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

Посредник vs Фасада

Фасад оговаривает вопрос структуры (кто кому подчиняется) а Посредник реализует эту структуру.

Хранитель

Также известен как: Memento, Снимок

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

Терминология

Паттерн считается реализованным, если в функционале появляется возможность восстанавливать состояние некого объекта (назовем его Originator), и достигнуто это с помощью трех классов:

  1. «Originator/Создатель» — объект/значение
  2. «Caretaker/Опекун» — объект, в который помещается Originator
  3. «Memento/Хранитель» — объект обертка Originator-а (рудемент, которого не должно быть в той интерпретации паттерна, которую я наблюдаю в настоящий момент в многих интернет-источниках)

OriginatorObject = new Originator(‘<"value":"1">‘) // создаем объект из переданного json

Caretake.set(OriginatorObject) // функция set преобразует OriginatorObject в json и сохранит его в себе

OriginatorObject.value = ‘<"value":"2">‘ // изменяем состояние объекта OriginatorObject

OriginatorObject = Caretake.get() // восстановление OriginatorObject с помощью внутреннего вызова new Originator(‘<"value":"1">‘)

Внимание, в примере нет объекта «Memento/Хранитель», потому что «Memento/Хранитель» это внутренний класс, который используется объектом Caretake, чтобы сохранить состояние объекта/значения Originator. Практически, вместо «Memento/Хранитель» может быть простая сериализация значения в json / из json.

Наблюдатель

Также известен как: Издатель-Подписчик, Слушатель, Observer

Для реализации паттерна Вам понадобится:

  • Event (Событие) — событие, которое может возникнуть (например пришло письмо)
  • Subject (Издатель) — объект, который уведомляет о событии
  • Observer (Подписчик/Слушатель) — объект, который подписывается на событие и ожидает уведомление

Подписчик vs Слушатель

  • Подписчик (Subscriber) — объект, который подписался сразу на несколько событий одного объекта (например человек подписался на два журнала одного издательства)
  • Слушатель ( Listener) — объект, который подписался только на одно событий одного объекта (например человек подписался на один журнала одного издательства)
  • push — когда Observer-у предоставляется вся информация о уведомлении (почтальон вручил ценную посылку прямо в руки человека)
  • pull — когда Observer подтягивает информация о уведомлении (человек получил уведомление о том, что ему пришла посылка, теперь он должен сходить на почту за посылкой)

Пример из жизни

Почтовое отделение и человек.

Нюанс

Иногда, Event (Событие) и Subject (Издатель) объединяют, пример из жизни: знаменитость может уведомить лично своих фанатов.

В программном смысле

Observer <
abstract public update ( SplSubject subject ) : void // этот метод будет вызван, когда в объекте Subject произойдет изменение состояния
>
Subject <
abstract public attach ( SplObserver observer ) : void
abstract public detach ( SplObserver observer ) : void
abstract public notify ( void ) : void // объект Subject при изменении своего состояния, должен вызывать метод update(this) каждого Observer-а
>

Паттерны проектирования

Перед началом статьи, я хочу предложить доклад своего товарища — Дениса Порпленко о паттернах проектирования в программировании:

А дальше продолжу более обще и расширенно, так как невозможно в одном докладе охватить всю тематику паттернов проектирования. Так же, как не охватить её и в одной статье.

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

Тематика паттернов очень широка, паттерны проектирования можно разделить на такие типы, как шаблоны для программирования, шаблоны архитектуры системы и шаблоны хранения данных. Это самые популярные паттерны, есть и другие. Ниже я их все перечислю.

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

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

Основные шаблоны программирования

Фундаментальные

Шаблон делегирования ( Delegation pattern ) — Объект внешне выражает некоторое поведение, но в реальности передаёт ответственность за выполнение этого поведения связанному объекту.
Шаблон функционального дизайна ( Functional design ) — Гарантирует, что каждый модуль компьютерной программы имеет только одну обязанность и исполняет её с минимумом побочных эффектов на другие части программы.
Неизменяемый интерфейс ( Immutable interface ) — Создание неизменяемого объекта.
Интерфейс ( Interface ) — Общий метод для структурирования компьютерных программ для того, чтобы их было проще понять.
Интерфейс-маркер ( Marker interface ) — В качестве атрибута (как пометки объектной сущности) применяется наличие или отсутствие реализации интерфейса-маркера. В современных языках программирования вместо этого могут применяться атрибуты или аннотации.
Контейнер свойств ( Property container ) — Позволяет добавлять дополнительные свойства для класса в контейнер (внутри класса), вместо расширения класса новыми свойствами.
Событийный шаблон ( Event channel ) — Расширяет шаблон Publish/Subscribe, создавая централизованный канал для событий. Использует объект-представитель для подписки и объект-представитель для публикации события в канале. Представитель существует отдельно от реального издателя или подписчика. Подписчик может получать опубликованные события от более чем одного объекта, даже если он зарегистрирован только на одном канале.

Порождающие шаблоны

Порождающие шаблоны ( Creational ) — шаблоны проектирования, которые абстрагируют процесс инстанцирования. Они позволяют сделать систему независимой от способа создания, композиции и представления объектов. Шаблон, порождающий классы, использует наследование, чтобы изменять инстанцируемый класс, а шаблон, порождающий объекты, делегирует инстанцирование другому объекту.
Абстрактная фабрика ( Abstract factory ) — Класс, который представляет собой интерфейс для создания компонентов системы.
Строитель ( Builder ) — Класс, который представляет собой интерфейс для создания сложного объекта.
Фабричный метод ( Factory method ) — Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать.
Отложенная инициализация ( Lazy initialization ) — Объект, инициализируемый во время первого обращения к нему.
Пул одиночек ( Multiton ) — Гарантирует, что класс имеет поименованные экземпляры объекта и обеспечивает глобальную точку доступа к ним.
Объектный пул ( Object pool ) — Класс, который представляет собой интерфейс для работы с набором инициализированных и готовых к использованию объектов.
Прототип ( Prototype ) — Определяет интерфейс создания объекта через клонирование другого объекта вместо создания через конструктор.
Получение ресурса есть инициализация ( Resource acquisition is initialization (RAII) ) — Получение некоторого ресурса совмещается с инициализацией, а освобождение — с уничтожением объекта.
Одиночка ( Singleton ) — Класс, который может иметь только один экземпляр.

Структурные шаблоны

Структурные шаблоны ( Structural ) определяют различные сложные структуры, которые изменяют интерфейс уже существующих объектов или его реализацию, позволяя облегчить разработку и оптимизировать программу.
Адаптер ( Adapter / Wrapper ) — Объект, обеспечивающий взаимодействие двух других объектов, один из которых использует, а другой предоставляет несовместимый с первым интерфейс.
Мост ( Bridge ) — Структура, позволяющая изменять интерфейс обращения и интерфейс реализации класса независимо.
Компоновщик ( Composite ) — Объект, который объединяет в себе объекты, подобные ему самому.
Декоратор или Обёртка ( Decorator ) или ( Wrapper ) — Класс, расширяющий функциональность другого класса без использования наследования.
Фасад ( Facade ) — Объект, который абстрагирует работу с несколькими классами, объединяя их в единое целое.
Единая точка входа ( Front controller ) — Обеспечивает унифицированный интерфейс для интерфейсов в подсистеме. Front Controller определяет высокоуровневый интерфейс, упрощающий использование подсистемы.
Приспособленец ( Flyweight ) — Это объект, представляющий себя как уникальный экземпляр в разных местах программы, но по факту не являющийся таковым.
Заместитель ( Proxy ) — Объект, который является посредником между двумя другими объектами, и который реализует/ограничивает доступ к объекту, к которому обращаются через него.

Поведенческие шаблоны

Поведенческие шаблоны ( Behavioral ) определяют взаимодействие между объектами, увеличивая таким образом его гибкость.
Цепочка обязанностей ( Chain of responsibility ) — Предназначен для организации в системе уровней ответственности.
Команда ( Command ) — Представляет действие. Объект команды заключает в себе само действие и его параметры.
Интерпретатор ( Interpreter ) — Решает часто встречающуюся, но подверженную изменениям, задачу.
Итератор ( Iterator ) — Представляет собой объект, позволяющий получить последовательный доступ к элементам объекта-агрегата без использования описаний каждого из объектов, входящих в состав агрегации.
Посредник ( Mediator ) — Обеспечивает взаимодействие множества объектов, формируя при этом слабую связанность и избавляя объекты от необходимости явно ссылаться друг на друга.
Хранитель ( Memento ) — Позволяет не нарушая инкапсуляцию зафиксировать и сохранить внутренние состояния объекта так, чтобы позднее восстановить его в этих состояниях.
Нулевой объект ( Null object ) — Предотвращает нулевые указатели, предоставляя объект «по умолчанию».
Наблюдатель ( Observer ) — Определяет зависимость типа «один ко многим» между объектами таким образом, что при изменении состояния одного объекта все зависящие от него оповещаются об этом событии.
Слуга ( Servant ) — Используется для обеспечения общей функциональности группе классов.
Спецификация ( Specification ) — Служит для связывания бизнес-логики.
Состояние ( State ) — Используется в тех случаях, когда во время выполнения программы объект должен менять своё поведение в зависимости от своего состояния.
Стратегия ( Strategy ) — Предназначен для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости.
Шаблонный метод ( Template method ) — Определяет основу алгоритма и позволяет наследникам переопределять некоторые шаги алгоритма, не изменяя его структуру в целом.
Посетитель ( Visitor ) — Описывает операцию, которая выполняется над объектами других классов. При изменении класса Visitor нет необходимости изменять обслуживаемые классы.
Простая политика — я знаю, что такой паттерн есть, но что он означает, пока не нашел. Если будет инфа — скиньте в комментариях.
Слушатель ( Event listener ) — аналогично
Одноразовый посетитель ( Single-serving visitor ) — Оптимизирует реализацию шаблона посетитель, который инициализируется, единожды используется, и затем удаляется.
Иерархический посетитель ( Hierarchical visitor ) — Предоставляет способ обхода всех вершин иерархической структуры данных (например, древовидной).

Шаблоны параллельного программирования

Используются для более эффективного написания многопоточных программ, и предоставляет готовые решения проблем синхронизации.
Активный объект ( Active Object ) — Служит для отделения потока выполнения метода от потока, в котором он был вызван. Использует шаблоны асинхронный вызов методов и планировщик.
Уклонитель ( Balking ) — Служит для выполнения действия над объектом только тогда, когда тот находится в корректном состоянии.
Привязка свойств ( Binding properties ) — Комбинирует несколько наблюдателей для обеспечения синхронизации свойств в различных объектах
Обмен сообщениями ( Messaging design pattern (MDP) ) — Позволяет компонентам и приложениям обмениваться информацией (сообщениями).
Блокировка с двойной проверкой ( Double-checked locking ) — Предназначен для уменьшения накладных расходов, связанных с получением блокировки.
Ассинхронные события ( Event-based asynchronous ) — Адресные проблемы с Асинхронным паттерном, которые возникают в программах с несколькими потоками.
Охраняемая приостановка ( Guarded suspension ) — Используется для блокировки выполнения действия над объектом только тогда, когда тот находится в корректном состоянии.
Полусинхронизация ( Half-Sync/Half-Async ) — пока нет данных про этот паттерн.
Лидеры ( Leaders/followers ) — пока нет данных про этот паттерн.
Замок ( Lock ) — Один поток блокирует ресурс для предотвращения доступа или изменения его другими потоками.
Монитор ( Monitor object ) — Объект, предназначенный для безопасного использования более чем одним потоком.
Реактор ( Reactor ) — Предназначен для синхронной передачи запросов сервису от одного или нескольких источников.
Блокировка чтение-запись ( Read write lock ) — Позволяет нескольким потокам одновременно считывать информацию из общего хранилища, но позволяя только одному потоку в текущий момент времени её изменять.
Планировщик ( Scheduler ) — Обеспечивает механизм реализации политики планирования, но при этом не зависящих ни от одной конкретной политики.
Пул потоков ( Thread pool ) — Предоставляет пул потоков для обработки заданий, представленных обычно в виде очереди.
Спецпотоковое хранилище ( Thread-specific storage ) — Служит для предоставления различных глобальных переменных для разных потоков.
Однопоточное выполнение ( Single thread execution ) — Препятствует конкурентному вызову метода, тем самым запрещая параллельное выполнение этого метода.
Кооперативный паттерн ( Cooperative pattern ) — Обеспечивает механизм безопасной остановки потоков исполнения, используя общий флаг для сигнализирования прекращения работы потоков.

Шаблоны архитектуры системы

Model-View-Controller (MVC) — Модель-представление-контроллер.
Model-View-Presenter
Model-View-View Model
Presentation-Abstraction-Control
Naked objects
Hierarchical Model–View–Controller

Enterprise шаблоны

Active Record — способ доступа к данным реляционных баз данных в объектно-ориентированном программировании.
Business Delegate
Composite Entity/Составная Сущность
Composite View
DAO (Data Access Object) Объект Доступа к Данным
Dispatcher View
Front Controller
Intercepting Filter
Registry
Service Activator
Service Locator/Локатор Службы
Service to Worker
Session Facade/Фасад Сессии
Transfer Object Assembler
Transfer Object/Объект Перемещения
Value List Handler/Обработчик Списка Значений
View Helper
Unit of Work

Другие типы шаблонов

Также на сегодняшний день существует ряд других шаблонов.
Хранилище ( Repository )
Carrier Rider Mapper описывают предоставление доступа к хранимой информации.
Аналитические шаблоны описывают основной подход для составления требований для программного обеспечения (requirement analysis) до начала самого процесса программной разработки
Коммуникационные шаблоны описывают процесс общения между отдельными участниками/сотрудниками организации
Организационные шаблоны описывают организационную иерархию предприятия/фирмы
Антипаттерны (Anti-Design-Patterns) описывают, как не следует поступать при разработке программ, показывая характерные ошибки в дизайне и в реализации

Паттерны проектирования

Категория «Паттерны проектирования»

В категории «Паттерны проектирования» содержат статьи с описанием структуры типичных программных конструкций.

Шаблон проектирования или паттерн (англ. design pattern) в разработке программного обеспечения — повторяемая архитектурная конструкция, представляющая собой решение проблемы проектирования в рамках некоторого часто возникающего контекста.

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

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

На наивысшем уровне существуют архитектурные шаблоны, они охватывают собой архитектуру всей программной системы.

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

Паттерны проектирования

Паттерн Адаптер (Adapter)

Идея паттерна Адаптер (Adapter)

Паттерн (шаблон) проектирования — это продуманный способ построения исходного кода программы для решения часто возникающих в повседневном программировании проблем проектирования. Иными словами, это уже придуманное решения, для типичной задачи. При этом паттерн не готовое решение, а просто алгоритм действий, который должен привести к желаемому результату. Давайте рассмотрим один из наиболее часто используемых структурных паттернов — Адаптер (Adapter).

Паттерны проектирования

Паттерн Декоратор (Decorator)

Идея паттерна Декоратор (Decorator)

Паттерн (шаблон) проектирования — это продуманный способ построения исходного кода программы для решения часто возникающих в повседневном программировании проблем проектирования. Иными словами, это уже придуманное решения, для типичной задачи. При этом паттерн не готовое решение, а просто алгоритм действий, который должен привести к желаемому результату. Давайте рассмотрим один из наиболее часто используемых структурных паттернов — Декоратор (Decorator).

Паттерны проектирования

Паттерн Наблюдатель (Observer)

Идея паттерна Наблюдатель (Observer)

Паттерн (шаблон) проектирования — это продуманный способ построения исходного кода программы для решения часто возникающих в повседневном программировании проблем проектирования. Иными словами, это уже придуманное решения, для типичной задачи. При этом паттерн не готовое решение, а просто алгоритм действий, который должен привести к желаемому результату. Давайте рассмотрим один из наиболее часто используемых поведенческих паттернов — Наблюдатель (Observer).

Паттерн Абстрактная фабрика C# (Abstract Factory)

Идея паттерна Абстрактная фабрика C#

Паттерн (шаблон) проектирования — это продуманный способ построения исходного кода программы для решения часто возникающих в повседневном программировании проблем проектирования. Иными словами, это уже придуманное решения, для типичной задачи. При этом паттерн не готовое решение, а просто алгоритм действий, который должен привести к желаемому результату. Давайте рассмотрим один из наиболее часто используемых порождающих паттернов — Абстрактная фабрика (Abstract Factory).

Паттерны проектирования

Паттерн Одиночка (Singleton pattern)

Идея паттерна проектирования Одиночка (Singleton)

Паттерн (шаблон) проектирования — это продуманный способ построения исходного кода программы для решения часто возникающих в повседневном программировании проблем проектирования. Иными словами, это уже придуманное решения, для типичной задачи. При этом паттерн не готовое решение, а просто алгоритм действий, который должен привести к желаемому результату. Давайте рассмотрим один из наиболее простых паттернов — Singleton (Одиночка).

Добавить комментарий
Именование Описание
Фабрика Не паттерн, а общая концепция, часто под этим словом подразумевают Простую фабрику
Простая фабрика Класс, в котором есть всего одна функция, которая создает различные объекты в зависимости от переданных в нее аргументов
Абстрактная фабрика Интерфейс или абстрактный класс, с функциями, которые нужно реализовать, чтобы создавать объекты одной группы. Например нам нужна фабрика создания мебели, которая умеет создавать: шкаф, диван, кресло. Все бы ничего, но людям нужны разные стили мебели: классический, современный, футуристичный. Поэтому, логично создать 3 фабрики, каждая фабрика будет уметь делать ровно три предмета (шкаф, диван, кресло), но каждая в своем стиле (классический, современный, футуристичный).
Фабричный метод Реализованная функция интерфейса или абстрактного класса, вызов которой создает объект
Создающий метод Функция класса, которая создает объект класса, в котором находится
Статический фабричный метод Статичная функция класса, которая создает объект класса, в котором находится
Одиночка Функция класса, которая создает объект класса, в котором находится. Но, если вызвать данную функцию повторно, то она возвратит объект, который был создан при первом вызове функции.
Строитель