Skip to content

VaLeraGav/patterns-php

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Patterns

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

Почему важно знать паттерны?

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

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

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

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

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

Ключевые принципы

  • Don’t repeat yourself - DRY;
  • Keep it simple stupid - KISS;
  • You ain’t gonna need it - YAGNI;

Solid

  • У класса должна быть только 1 обязанность. Только 1 причина для изменения. Single Responsibility S_olid;
  • Классы открыты для расширения, но закрыты для изменения. Open/closed s_O_lid;
  • Наследующий класс должен дополнять, а не замещать поведение базового класса. Liskov Substitution so_L_id;
  • Клиенты не должны зависеть от методов, которые они не используют. Interface Segregation sol_I_d;
  • Инверсия управления (Голливудский принцип) ->>> Dependency-Inversion soli_D;

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

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

Одиночка (singleton)

▶ code

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

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

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

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

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

Преимущества:

  • Гарантирует наличие единственного экземпляра класса.
  • Предоставляет к нему глобальную точку доступа.
  • Реализует отложенную инициализацию объекта-одиночки.

Недостатки:

  • Нарушает принцип единственной ответственности класса.
  • Маскирует плохой дизайн.
  • Проблемы мультипоточности.
  • Требует постоянного создания Mock-объектов при юнит-тестирования.
Связи с другими паттернами
  • Фасад можно сделать Одиночкой, так как обычно нужен только один объект-фасад.
  • Паттерн Легковес может напоминать Одиночку, если для конкретной задачи у вас получилось свести количество объектов к одному. Но помните, что между этими паттернами есть два кардинальных отличия:
  • В отличие от Одиночки, вы можете иметь множество объектов-легковесов. Объекты-легковесы должны быть неизменяемыми, тогда как объект-одиночка допускает изменение своего состояния.
  • Абстрактная фабрика, Строитель и Прототип могут быть реализованы при помощи Одиночки.

⏏ В начало

Ленивая инициализация (Lazy Loading)

▶ code

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

Использование: Данный паттерн служит для оптимизации ресурсов.

Преимущества:

Недостатки:

⏏ В начало

Простая фабрика (Simple factory)

▶ code

Простая фабрика просто создает экземпляр для клиента, не предоставляя клиенту какой-либо логики создания.

Использование: Когда создание объекта — это не просто несколько присвоений, а какая-то логика, тогда имеет смысл создать отдельную фабрику вместо повторения одного и того же кода повсюду.

⏏ В начало

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

▶ code

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

Этот паттерн является «настоящим» Шаблоном Проектирования, потому что он следует «Принципу инверсии зависимостей» также известному как «D» в S.O.L.I.D.

Это означает, что класс FactoryMethod зависит от абстракций, а не от конкретных классов. Это существенный плюс в сравнении с SimpleFactory или StaticFactory.

Алгоритм реализации этого паттерна выглядит следующим образом:

  1. Необходимо унифицировать интерфейсы всех создаваемых объектов.
  2. В классе, который производит продукты, создайте пустой "Фабричный метод".
  3. В качестве возвращаемого типа укажите общий интерфейс продукта.
  4. Переопределите "Фабричный метод" в подклассах, перемещая туда создание соответствующих продуктов.
  5. Если создаваемых продуктов слишком много, целесообразно задуматься о введении параметров в "Фабричный метод", которые позволят возвращать различные продукты в пределах одного подкласса.

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

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

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

Использование:

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

Преимущества:

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

Недостатки:

  • Код может стать более сложным, поскольку вам нужно ввести много новых подклассов для реализации шаблона. В идеале вы вводите этот паттерн в существующую иерархию классов-создателей.
Связи с другими паттернами
  • Многие архитектуры начинаются с применения Фабричного метода (более простого и расширяемого через подклассы) и эволюционируют в сторону Абстрактной фабрики (Abstract Factory), Прототипа (Prototype) или Строителя (Builder) (более гибких, но и более сложных).
  • Классы Абстрактной фабрики чаще всего реализуются с помощью Фабричного метода, хотя они могут быть построены и на основе Прототипа.
  • Фабричный метод можно использовать вместе с Итератором (Iterator), чтобы подклассы коллекций могли создавать подходящие им итераторы.
  • Прототип не опирается на наследование, поэтому у него нет ее недостатков. Но ему нужна сложная операция инициализации. Фабричный метод, наоборот, построен на наследовании, но не требует сложной инициализации.
  • Фабричный метод можно рассматривать как частный случай Шаблонного метода (Template Method). Кроме того, Фабричный метод нередко бывает частью большого Шаблонного метода.

⏏ В начало

Статичная фабрика (Static factory)

▶ code

Статический фабричный метод — это просто статический метод, который возвращает экземпляр класса. Она отличается от Простой Фабрики тем, что собственно является статической.

⏏ В начало

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

▶ code

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

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

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

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

Для того чтобы реализация "Абстрактной фабрики" была выполнена, необходимо следовать следующим рекомендациям:

  1. Однозначно идентифицируйте правила создания экземпляров.
  2. Определите абстрактный класс с интерфейсом, включающим отдельный метод для каждого из классов, экземпляры которых должны быть созданы.
  3. Для каждого семейства создайте конкретные классы, производные отданного абстрактного класса.
  4. Использующий экземпляры объект должен обращаться к "Абстрактной фабрике" для создания требуемых экземпляров.

Использование:

Преимущества:

  • Обеспечивает гибкость в создании объектов связанных с определенной темой.
  • Инкапсулирует объекты, созданные в соответствии с определенной темой.
  • Позволяет заменять отдельные объекты без изменения всей структуры кода.
  • Реализует принцип Open/closed.

Недостатки:

  • При добавлении новых продуктов в семейство необходимо изменять интерфейсы всех фабрик.
  • Сложность в реализации при наличии большого количества объектов.
  • Требует наличия всех типов продуктов в каждой вариации.
  • Усложняет код программы за счёт множества дополнительных классов.
Связи с другими паттернами
  • Многие архитектуры начинаются с применения Фабричного метода (более простого и расширяемого через подклассы) и эволюционируют в сторону Абстрактной фабрики, Прототипа или Строителя (более гибких, но и более сложных).
  • Строитель концентрируется на построении сложных объектов шаг за шагом. Абстрактная фабрика специализируется на создании семейств связанных продуктов. Строитель возвращает продукт только после выполнения всех шагов, а Абстрактная фабрика возвращает продукт сразу же.
  • Классы Абстрактной фабрики чаще всего реализуются с помощью Фабричного метода, хотя они могут быть построены и на основе Прототипа.
  • Абстрактная фабрика может быть использована вместо Фасада для того, чтобы скрыть платформо-зависимые классы.
  • Абстрактная фабрика может работать совместно с Мостом. Это особенно полезно, если у вас есть абстракции, которые могут работать только с некоторыми из реализаций. В этом случае фабрика будет определять типы создаваемых абстракций и реализаций.
  • Абстрактная фабрика, Строитель и Прототип могут быть реализованы при помощи Одиночки.

⏏ В начало

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

▶ code

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

Сказав это, позвольте мне добавить немного о том, что такое анти-шаблон телескопического конструктора. В тот или иной момент мы все видели конструктор, как показано ниже:

public function __construct($size, $cheese = true, $pepperoni = true, $tomato = false, $lettuce = true)
{
}

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

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

ПримечаниеФабрика» — это автомат по продаже напитков, в нем уже есть всё готовое (или «осталось разогреть»), а вы только говорите что вам нужно (нажимаете кнопку). «Строитель» — это завод, который производит эти напитки и содержит в себе все сложные операции и может собирать сложные объекты из более простых (упаковка, этикетка, вода, ароматизаторы и т.п.) в зависимости от запроса.

Использование:

  • Когда вы хотите избавиться от «телескопического конструктора».
  • Когда ваш код должен создавать разные представления какого-то объекта. Например, деревянные и железобетонные дома.
  • Когда вам нужно собирать сложные составные объекты, например, деревья Компоновщика.

Преимущества:

  • Позволяет создавать продукты пошагово.
  • Позволяет использовать один и тот же код для создания различных продуктов.
  • Изолирует сложный код сборки продукта от его основной бизнес-логики.

Недостатки:

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

⏏ В начало

Прототип (Prototype)

▶ code

Этот шаблон объявляет интерфейс для клонирования самого себя. Логика приложения, создавая новый объект, обращается к " Прототипу" с запросом его клонирования.

Отличительными условиями, характеризующими уместность применения шаблона "Прототип", являются:

  1. Определение создаваемого объекта динамически, во время выполнения.
  2. Нежелательное создание отдельной иерархии классов фабрик для создания объектов-продуктов из параллельной иерархии классов (отличие от шаблона "Абстрактная фабрика").
  3. Функционал клонирования объекта является более предпочтительным вариантом, нежели его создание и инициализация с помощью конструктора.
  4. Особенно в ситуациях, когда объект может принимать небольшое ограниченное число возможных состояний.

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

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

Использование:

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

Преимущества:

  • Позволяет клонировать объекты, не привязываясь к их конкретным классам.
  • Меньше повторяющегося кода инициализации объектов.
  • Ускоряет создание объектов.
  • Альтернатива созданию подклассов для конструирования сложных объектов.

Недостатки:

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

⏏ В начало

Бассейн (Object pool)

▶ code

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

Преимущества

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

Когда использовать шаблон проектирования пула объектов

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

Использование:

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

Преимущества:

  • Повышает производительность приложения
  • Управляет соединениями и обеспечивает способ повторного использования и обмена ими
  • Может предоставить ограничение для максимального количества объектов, которые могут быть созданы

Недостатки:

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

⏏ В начало


Структурные

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

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

Внедрение Зависимости (Dependency Injection)

▶ code

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

Использование:

Преимущества:

Недостатки:

⏏ В начало

Реестр (Registry)

▶ code

это вводит глобальное состояние в вашем приложении, которое не может быть смоделировано для тестирования и поэтому считается анти-шаблоном! Вместо этого используйте внедрение зависимостей!

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

Использование:

Преимущества:

Недостатки:

⏏ В начало

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

▶ code

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

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

Достоинствами шаблона "Компоновщик" являются:

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

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

Использование:

  • Когда вам нужно представить древовидную структуру объектов.
  • Когда клиенты должны единообразно трактовать простые и составные объекты.

Преимущества:

  • Упрощает архитектуру клиента при работе со сложным деревом компонентов.
  • Облегчает добавление новых видов компонентов.

Недостатки:

  • Создаёт слишком общий дизайн классов.
Связи с другими паттернами Строитель позволяет пошагово сооружать дерево Компоновщика.

Цепочку обязанностей часто используют вместе с Компоновщиком. В этом случае запрос передаётся от дочерних компонентов к их родителям.

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

Вы можете выполнить какое-то действие над всем деревом Компоновщика при помощи Посетителя.

Компоновщик часто совмещают с Легковесом, чтобы реализовать общие ветки дерева и сэкономить при этом память.

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

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

Но они могут и сотрудничать: Компоновщик может использовать Декоратор, чтобы переопределить функции отдельных частей дерева компонентов.

Архитектура, построенная на Компоновщиках и Декораторах, часто может быть улучшена за счёт внедрения Прототипа. Он позволяет клонировать сложные структуры объектов, а не собирать их заново.

⏏ В начало

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

▶ code

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

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

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

Использование:

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

Преимущества:

  • Отделяет и скрывает от клиента подробности преобразования различных интерфейсов.

Недостатки:

  • Усложняет код программы из-за введения дополнительных классов.
Связи с другими паттернами
  • Мост проектируют загодя, чтобы развивать большие части приложения отдельно друг от друга. Адаптер применяется постфактум, чтобы заставить несовместимые классы работать вместе.
  • Адаптер меняет интерфейс существующего объекта. Декоратор улучшает другой объект без изменения его интерфейса. Причём Декоратор поддерживает рекурсивную вложенность, чего не скажешь об Адаптере.
  • Адаптер предоставляет классу альтернативный интерфейс. Декоратор предоставляет расширенный интерфейс. Заместитель предоставляет тот же интерфейс.
  • Фасад задаёт новый интерфейс, тогда как Адаптер повторно использует старый. Адаптер оборачивает только один класс, а Фасад оборачивает целую подсистему. Кроме того, Адаптер позволяет двум существующим интерфейсам работать сообща, вместо того, чтобы задать полностью новый.
  • Мост, Стратегия и Состояние (а также слегка и Адаптер) имеют схожие структуры классов – все они построены на принципе «композиции», то есть делегирования работы другим объектам. Тем не менее, они отличаются тем, что решают разные проблемы. Помните, что паттерны – это не только рецепт построения кода определённым образом, но и описание проблем, которые привели к данному решению.

⏏ В начало

Мост (Bridge)

▶ code

Шаблон моста предполагает предпочтение композиции наследованию. Детали реализации передаются из иерархии в другой объект с отдельной иерархией.

Таким образом, если требуется на уровне приложения разделять абстракцию и реализацию так, чтобы и то и другое можно было изменять независимо, применяют паттерн "Мост"

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

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

Использование:

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

Преимущества:

  • Позволяет строить платформо-независимые программы.
  • Скрывает лишние или опасные детали реализации от клиентского кода.
  • Реализует принцип открытости/закрытости.

Недостатки:

  • Усложняет код программы из-за введения дополнительных классов.
Связи с другими паттернами - Мост проектируют загодя, чтобы развивать большие части приложения отдельно друг от друга. Адаптер применяется постфактум, чтобы заставить несовместимые классы работать вместе. - Мост, Стратегия и Состояние (а также слегка и Адаптер) имеют схожие структуры классов – все они построены на принципе «композиции», то есть делегирования работы другим объектам. Тем не менее, они отличаются тем, что решают разные проблемы. Помните, что паттерны – это не только рецепт построения кода определённым образом, но и описание проблем, которые привели к данному решению. - Абстрактная фабрика может работать совместно с Мостом. Это особенно полезно, если у вас есть абстракции, которые могут работать только с некоторыми из реализаций. В этом случае фабрика будет определять типы создаваемых абстракций и реализаций. - Паттерн Строитель может быть построен в виде Моста: директор будет играть роль абстракции, а строители – реализации.

⏏ В начало

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

▶ code

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

Ключевым моментом этого паттерна, в отличие от Активной Записи (Active Records) является то, что модель данных следует Принципу Единой Обязанности SOLID.

Использование:

Преимущества:

Недостатки:

⏏ В начало

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

▶ code

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

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

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

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

Использование:

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

Преимущества:

  • Большая гибкость, чем у наследования.
  • Позволяет добавлять обязанности на лету.
  • Можно добавлять несколько новых обязанностей сразу.
  • Позволяет иметь несколько мелких объектов вместо одного объекта на все случаи жизни.

Недостатки:

  • Трудно конфигурировать многократно обёрнутые объекты.
  • Обилие крошечных классов.
Связи с другими паттернами

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

Адаптер предоставляет классу альтернативный интерфейс. Декоратор предоставляет расширенный интерфейс. Заместитель предоставляет тот же интерфейс.

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

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

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

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

Но они могут и сотрудничать: Компоновщик может использовать Декоратор, чтобы переопределить функции отдельных частей дерева компонентов.

Архитектура, построенная на Компоновщиках и Декораторах, часто может быть улучшена за счёт внедрения Прототипа. Он позволяет клонировать сложные структуры объектов, а не собирать их заново.

Стратегия меняет поведение объекта «изнутри», а Декоратор изменяет его «снаружи».

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

⏏ В начало

Фасад (Facade)

▶ code

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

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

Примечание: Хороший фасад не содержит созданий экземпляров классов (new) внутри. Если внутри фасада создаются объекты для реализации каждого метода, это не Фасад, это Строитель или [Абстрактная|Статическая|Простая] Фабрика [или Фабричный Метод].

Использование:

  • Когда вам нужно представить простой или урезанный интерфейс к сложной подсистеме.
  • Когда вы хотите разложить подсистему на отдельные слои.

Преимущества:

  • Изолирует клиентов от компонентов сложной подсистемы.

Недостатки:

  • Фасад рискует стать божественным объектом, привязанным ко всем классам программы.
Связи с другими паттернами
  • Фасад задаёт новый интерфейс, тогда как Адаптер повторно использует старый. Адаптер оборачивает только один класс, а Фасад оборачивает целую подсистему. Кроме того, Адаптер позволяет двум существующим интерфейсам работать сообща, вместо того, чтобы задать полностью новый.
  • Абстрактная фабрика может быть использована вместо Фасада для того, чтобы скрыть платформо-зависимые классы.
  • Легковес показывает, как создавать много мелких объектов, а Фасад показывает, как создать один объект, который отображает целую подсистему.
  • Посредник и Фасад похожи тем, что пытаются организовать работу множества существующих классов.
    • Фасад создаёт упрощённый интерфейс к подсистеме, не внося в неё никакой добавочной функциональности. Сама подсистема не знает о существовании Фасада. Классы подсистемы общаются друг с другом напрямую.
    • Посредник централизует общение между компонентами системы. Компоненты системы знают только о существовании Посредника, у них нет прямого доступа к другим компонентам.
  • Фасад можно сделать Одиночкой, так как обычно нужен только один объект-фасад.
  • Фасад похож на Заместитель тем, что замещает сложную подсистему и может сам её инициализировать. Но в отличие от Фасада, Заместитель имеет тот же интерфейс, что его служебный объект, благодаря чему их можно взаимозаменять.

⏏ В начало

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

▶ code

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

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

Использование:

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

Преимущества:

  • Экономит оперативную память.

Недостатки:

  • Расходует процессорное время на поиск/вычисление контекста.
  • Усложняет код программы из-за введения множества дополнительных классов.
Связи с другими паттернами
  • Компоновщик часто совмещают с Легковесом, чтобы реализовать общие ветки дерева и сэкономить при этом память.
  • Легковес показывает, как создавать много мелких объектов, а Фасад показывает, как создать один объект, который отображает целую подсистему.
  • Паттерн Легковес может напоминать Одиночку, если для конкретной задачи у вас получилось свести количество объектов к одному. Но помните, что между паттернами есть два кардинальных отличия:
    • В отличие от Одиночки, вы можете иметь множество объектов-легковесов.
    • Объекты-легковесы должны быть неизменяемыми, тогда как объект-одиночка допускает изменение своего состояния.

⏏ В начало

Прокси (Proxy)

▶ code

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

Использование:

  • Ленивая инициализация (виртуальный прокси). Когда у вас есть тяжёлый объект, грузящий данные из файловой системы или базы данных.
  • Защита доступа (защищающий прокси). Когда в программе есть разные типы пользователей, и вам хочется защищать объект от неавторизованного доступа. Например, если ваши объекты – это важная часть операционной системы, а пользователи – сторонние программы (хорошие или вредоносные).
  • Локальный запуск сервиса (удалённый прокси). Когда настоящий сервисный объект находится на удалённом сервере.
  • Логирование запросов (логирующий прокси). Когда требуется хранить историю обращений к сервисному объекту.
  • Кеширование объектов («умная» ссылка). Когда нужно кешировать результаты запросов клиентов и управлять их жизненным циклом.

Преимущества:

  • Позволяет контролировать сервисный объект незаметно для клиента.
  • Может работать, даже если сервисный объект ещё не создан.
  • Может контролировать жизненный цикл служебного объекта.

Недостатки:

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

⏏ В начало


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

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

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

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

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

▶ code

Состояние - инкапсулирует изменение поведения одних и тех же методов в зависимости от состояния объекта. Этот паттерн поможет изящным способом изменить поведение объекта во время выполнения не прибегая к большим монолитным условным операторам.

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

Использование:

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

Преимущества:

  • Избавляет от множества больших условных операторов машины состояний.
  • Концентрирует в одном месте код, связанный с определённым состоянием.
  • Упрощает код контекста.

Недостатки:

  • Может неоправданно усложнить код, если состояний мало и они редко меняются.
Связи с другими паттернами
  • Мост, Стратегия и Состояние (а также слегка и Адаптер) имеют схожие структуры классов – все они построены на принципе «композиции», то есть делегирования работы другим объектам. Тем не менее, они отличаются тем, что решают разные проблемы. Помните, что паттерны – это не только рецепт построения кода определённым образом, но и описание проблем, которые привели к данному решению.
  • Состояние можно рассматривать как надстройку над Стратегией. Оба паттерна используют композицию, чтобы менять поведение основного объекта, делегируя работу вложенным объектам-помощникам. Однако в Стратегии эти объекты не знают друг о друге и никак не связаны. В Состоянии сами конкретные состояния могут переключать контекст.

⏏ В начало

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

▶ code

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

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

К основным преимуществам использования этого шаблона следует отнести следующие:

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

Использование:

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

Преимущества:

  • Горячая замена алгоритмов на лету.
  • Изолирует код и данные алгоритмов от остальных классов.
  • Уход от наследования к делегированию.
  • Реализует принцип открытости/закрытости.

Недостатки:

  • Усложняет программу за счёт дополнительных классов.
  • Клиент должен знать, в чём состоит разница между стратегиями, чтобы выбрать подходящую.
Связи с другими паттернами
  • Мост, Стратегия и Состояние (а также слегка и Адаптер) имеют схожие структуры классов – все они построены на принципе «композиции», то есть делегирования работы другим объектам. Тем не менее, они отличаются тем, что решают разные проблемы. Помните, что паттерны – это не только рецепт построения кода определённым образом, но и описание проблем, которые привели к данному решению.
  • Команда и Стратегия похожи по духу, но отличаются масштабом и применением:
    • Команду используют, чтобы превратить любые разнородные действия в объекты. Параметры операции превращаются в поля объекта. Этот объект теперь можно логировать, хранить в истории для отмены, передавать во внешние сервисы и так далее.
    • С другой стороны, Стратегия описывает разные способы произвести одно и то же действие, позволяя взаимозаменять эти способы в каком-то объекте контекста.
  • Стратегия меняет поведение объекта «изнутри», а Декоратор изменяет его «снаружи».
  • Шаблонный метод использует наследование, чтобы расширять части алгоритма. Стратегия использует делегирование, чтобы изменять выполняемые алгоритмы на лету. Шаблонный метод работает на уровне классов. Стратегия позволяет менять логику отдельных объектов.
  • Состояние можно рассматривать как надстройку над Стратегией. Оба паттерна используют композицию, чтобы менять поведение основного объекта, делегируя работу вложенным объектам-помощникам. Однако в Стратегии эти объекты не знают друг о друге и никак не связаны. В Состоянии сами конкретные состояния могут переключать контекст.

⏏ В начало

Объект Null (Null Object)

▶ code

Методы, которые возвращают объект или Null, вместо этого должны вернуть объект NullObject. Это упрощённый формальный код, устраняющий необходимость проверки if (!is_null($obj)) { $obj->callSomething(); }, заменяя её на обычный вызов $obj->callSomething();.

Использование:

Преимущества:

Недостатки:

Связи с другими паттернами

⏏ В начало

Команда (Command)

▶ code

Позволяет инкапсулировать действия в объекты. Основная идея этого шаблона — предоставить средства для отделения клиента от получателя.

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

Использование:

  • Когда вы хотите параметризовать объекты выполняемым действием.
  • Когда вы хотите ставить операции в очередь, выполнять их по расписанию или передавать по сети (Сетевые распределенные системы, использующие запросы в виде объектов в качестве основного примитива инициализации каких-либо операций)
  • Когда вам нужна операция отмены. (Любое приложение c возможностями отмены или повторения действий (undo/redo) пользователя)

Преимущества:

  • Убирает прямую зависимость между объектами, вызывающими операции, и объектами, которые их непосредственно выполняют.
  • Позволяет реализовать простую отмену и повтор операций.
  • Позволяет реализовать отложенный запуск операций.
  • Позволяет собирать сложные команды из простых.
  • Реализует принцип открытости/закрытости.

Недостатки:

  • Усложняет код программы из-за введения множества дополнительных классов.
Связи с другими паттернами
  • Цепочка обязанностей, Команда, Посредник и Наблюдатель показывают различные способы работы отправителей запросов с их получателями:
    • Цепочка обязанностей передаёт запрос последовательно через цепочку потенциальных получателей, ожидая, что какой-то из них обработает запрос.
    • Команда устанавливает косвенную одностороннюю связь от отправителей к получателям.
    • Посредник убирает прямую связь между отправителями и получателями, заставляя их общаться опосредованно, через себя.
    • Наблюдатель передаёт запрос одновременно всем заинтересованным получателям, но позволяет им динамически подписываться или отписываться от таких оповещений.
  • Обработчики в Цепочке обязанностей могут быть выполнены в виде Команд. В этом случае множество разных операций может быть выполнено над одним и тем же контекстом, коим является запрос.
    • Но есть и другой подход, в котором сам запрос является Командой, посланной по цепочке объектов. В этом случае одна и та же операция может быть выполнена над множеством разных контекстов, представленных в виде цепочки.
  • Команду и Снимок можно использовать сообща для реализации отмены операций. В этом случае объекты команд будут отвечать за выполнение действия над объектом, а снимки будут хранить резервную копию состояния этого объекта, сделанную перед запуском команды.
  • Команда и Стратегия похожи по духу, но отличаются масштабом и применением:
    • Команду используют, чтобы превратить любые разнородные действия в объекты. Параметры операции превращаются в поля объекта. Этот объект теперь можно логировать, хранить в истории для отмены, передавать во внешние сервисы и так далее.
    • С другой стороны, Стратегия описывает разные способы произвести одно и то же действие, позволяя взаимозаменять эти способы в каком-то объекте контекста.
  • Если Команду нужно копировать перед вставкой в историю выполненных команд, вам может помочь Прототип.
  • Посетитель можно рассматривать как расширенный аналог Команды, который способен работать сразу с несколькими видами получателей.

⏏ В начало

Итератор (Iterator)

▶ code

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

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

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

Использование:

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

Преимущества:

  • Упрощает классы хранения данных.
  • Позволяет реализовать различные способы обхода структуры данных.
  • Позволяет одновременно перемещаться по структуре данных в разные стороны.

Недостатки:

  • Не оправдан, если можно обойтись простым циклом.
Связи с другими паттернами
  • Вы можете обходить дерево Компоновщика, используя Итератор.
  • Фабричный метод можно использовать вместе с Итератором, чтобы подклассы коллекций могли создавать подходящие им итераторы.
  • Снимок можно использовать вместе с Итератором, чтобы сохранить текущее состояние обхода структуры данных и вернуться к нему в будущем, если потребуется.
  • Посетитель можно использовать совместно с Итератором. Итератор будет отвечать за обход структуры данных, а Посетитель – за выполнение действий над каждым её компонентом.

⏏ В начало

Спецификация (Specification)

▶ code

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

Использование:

Преимущества:

Недостатки:

⏏ В начало

Цепочка Обязанностей (Chain Of Responsibilities)

▶ code

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

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

Шаблон "Цепочка обязанностей" позволяет:

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

Использование:

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

Преимущества:

  • Уменьшает зависимость между клиентом и обработчиками.
  • Реализует принцип единственной обязанности.
  • Реализует принцип открытости/закрытости.

Недостатки:

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

⏏ В начало

Локатор Служб (Service Locator)

▶ code

Некоторые считают Локатор Служб анти-паттерном. Он нарушает принцип инверсии зависимостей (Dependency Inversion principle) из набора принципов SOLID. Локатор Служб скрывает зависимости данного класса вместо их совместного использования, как в случае шаблона Внедрение Зависимости (Dependency Injection). В случае изменения данных зависимостей мы рискуем сломать функционал классов, которые их используют, вследствие чего затрудняется поддержка системы.

Для реализации слабосвязанной архитектуры, чтобы получить хорошо тестируемый, сопровождаемый и расширяемый код. Паттерн Инъекция зависимостей (DI) и паттерн Локатор Служб — это реализация паттерна Инверсия управления (Inversion of Control, IoC).

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

Использование:

Преимущества:

Недостатки:

⏏ В начало

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

▶ code

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

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

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

Использование:

Преимущества:

Недостатки:

⏏ В начало

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

▶ code

Этот паттерн позволяет снизить связность множества компонентов, работающих совместно. Объектам больше нет нужды вызывать друг друга напрямую. Это хорошая альтернатива Наблюдателю, если у вас есть “центр интеллекта” вроде контроллера (но не в смысле MVC)

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

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

Использование:

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

Преимущества:

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

Недостатки:

  • Посредник может сильно раздуться.
  • Модули больше не могут взаимодействовать напрямую.
  • Использование медиатора приводит к предсказуемому падению производительности–из-за слабой связанности становится достаточно трудно определить реакцию системы, отталкиваясь только от событий, происходящих в ней.
Связи с другими паттернами
  • Цепочка обязанностей, Команда, Посредник и Наблюдатель показывают различные способы работы отправителей запросов с их получателями:
    • Цепочка обязанностей передаёт запрос последовательно через цепочку потенциальных получателей, ожидая, что какой-то из них обработает запрос.
    • Команда устанавливает косвенную одностороннюю связь от отправителей к получателям.
    • Посредник убирает прямую связь между отправителями и получателями, заставляя их общаться опосредованно, через себя.
    • Наблюдатель передаёт запрос одновременно всем заинтересованным получателям, но позволяет им динамически подписываться или отписываться от таких оповещений.
  • Посредник и Фасад похожи тем, что пытаются организовать работу множества существующих классов.
    • Фасад создаёт упрощённый интерфейс к подсистеме, не внося в неё никакой добавочной функциональности. Сама подсистема не знает о существовании Фасада. Классы подсистемы общаются друг с другом напрямую.
    • Посредник централизует общение между компонентами системы. Компоненты системы знают только о существовании Посредника, у них нет прямого доступа к другим компонентам.
  • Разница между Посредником и Наблюдателем не всегда очевидна. Чаще всего они выступают как конкуренты, но иногда могут работать вместе.
    • Цель Посредника – убрать обоюдные зависимости между компонентами системы. Вместо этого они становятся зависимыми от самого посредника. С другой стороны, цель Наблюдателя – обеспечить динамическую одностороннюю связь, в которой одни объекты косвенно зависят от других.
    • Довольно популярна реализация Посредника при помощи Наблюдателя. При этом объект посредника будет выступать издателем, а все остальные компоненты станут подписчиками и смогут динамически следить за событиями, происходящими в посреднике. В этом случае трудно понять, чем же отличаются оба паттерна.
    • Но Посредник имеет и другие реализации, когда отдельные компоненты жёстко привязаны к объекту посредника. Такой код вряд ли будет напоминать Наблюдателя, но всё же останется Посредником.
    • Напротив, в случае реализации посредника с помощью Наблюдателя представим такую программу, в которой каждый компонент системы становится издателем. Компоненты могут подписываться друг на друга, в то же время не привязываясь к конкретным классам. Программа будет состоять из целой сети Наблюдателей, не имея центрального объекта-Посредника.

⏏ В начало

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

▶ code

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

Шаблон Хранитель реализуется тремя объектами: «Смотритель», «Хранитель» и «Хозяин».

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

Использование:

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

Преимущества:

  • Не нарушает инкапсуляции исходного объекта.
  • Упрощает структуру исходного объекта. Ему не нужно хранить историю версий своего состояния.

Недостатки:

  • Требует много памяти, если клиенты слишком часто создают снимки.
  • Может повлечь дополнительные издержки памяти, если объекты, хранящие историю, не освобождают ресурсы, занятые устаревшими снимками.
  • В некоторых языках (например, PHP, Python, JavaScript) сложно гарантировать, чтобы только исходный объект имел доступ к состоянию снимка.
Связи с другими паттернами:
  • Команду и Снимок можно использовать сообща для реализации отмены операций. В этом случае объекты команд будут отвечать за выполнение действия над объектом, а снимки будут хранить резервную копию состояния этого объекта, сделанную перед самым запуском команды.
  • Снимок можно использовать вместе с Итератором, чтобы сохранить текущее состояние обхода структуры данных и вернуться к нему в будущем, если потребуется.
  • Снимок иногда можно заменить Прототипом, если объект, состояние которого требуется сохранять в истории, довольно простой, не имеет активных ссылок на внешние ресурсы, либо их можно легко восстановить.

⏏ В начало

Хранилище (Repository)

▶ code

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

Использование:

Преимущества:

Недостатки:

⏏ В начало

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

▶ code

Для реализации публикации/подписки на поведение объекта, всякий раз, когда объект «Subject» меняет свое состояние, прикрепленные объекты «Observers» будут уведомлены. Паттерн используется, чтобы сократить количество связанных напрямую объектов и вместо этого использует слабую связь (loose coupling).

Использование:

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

Преимущества:

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

Недостатки:

  • Подписчики оповещаются в случайном порядке.
  • Непредвиденные обновления.
  • Изменение объекта может вызвать каскад зависимых от него наблюдателей с высокой стоимостью обновления.
  • Протокол обновления не содержит никаких сведений о том, что изменилось в субъекте,– работа наблюдателей при этом усложняется.
Связи с другими паттернами
  • Цепочка обязанностей, Команда, Посредник и Наблюдатель показывают различные способы работы отправителей запросов с их получателями:
    • Цепочка обязанностей передаёт запрос последовательно через цепочку потенциальных получателей, ожидая, что какой-то из них обработает запрос.
    • Команда устанавливает косвенную одностороннюю связь от отправителей к получателям.
    • Посредник убирает прямую связь между отправителями и получателями, заставляя их общаться опосредованно, через себя.
    • Наблюдатель передаёт запрос одновременно всем заинтересованным получателям, но позволяет им динамически подписываться или отписываться от таких оповещений.
  • Разница между Посредником и Наблюдателем не всегда очевидна. Чаще всего они выступают как конкуренты, но иногда могут работать вместе.
    • Цель Посредника – убрать обоюдные зависимости между компонентами системы. Вместо этого они становятся зависимыми от самого посредника. С другой стороны, цель Наблюдателя – обеспечить динамическую одностороннюю связь, в которой одни объекты косвенно зависят от других.
    • Довольно популярна реализация Посредника при помощи Наблюдателя. При этом объект посредника будет выступать издателем, а все остальные компоненты станут подписчиками и смогут динамически следить за событиями, происходящими в посреднике. В этом случае трудно понять, чем же отличаются оба паттерна.
    • Но Посредник имеет и другие реализации, когда отдельные компоненты жёстко привязаны к объекту посредника. Такой код вряд ли будет напоминать Наблюдателя, но всё же останется Посредником.
    • Напротив, в случае реализации посредника с помощью Наблюдателя представим такую программу, в которой каждый компонент системы становится издателем. Компоненты могут подписываться друг на друга, в то же время не привязываясь к конкретным классам. Программа будет состоять из целой сети Наблюдателей, не имея центрального объекта-Посредника.

⏏ В начало

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

▶ code

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

"Шаблонный метод" определяет основной алгоритм и позволяет подклассам изменить некоторые шаги этого алгоритма без изменения его общей структуры.

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

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

Использование:

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

Преимущества:

  • Облегчает повторное использование кода.

Недостатки:

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

Фабричный метод можно рассматривать как частный случай Шаблонного метода. Кроме того, Фабричный метод нередко бывает частью большого класса с Шаблонными методами.

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

⏏ В начало

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

▶ code

Шаблон «Посетитель» выполняет операции над объектами других классов. Главной целью является сохранение разделения направленности задач отдельных классов. При этом классы обязаны определить специальный контракт, чтобы позволить использовать их Посетителям (метод «принять роль» Role::accept в примере).

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

Главными преимуществами применения этого паттерна являются следующие:

  • Упрощается добавление новых операций к существующим в системе объектам.
  • Он объединяет родственные операции в едином классе.

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

Использование:

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

Преимущества:

  • Упрощает добавление операций, работающих со сложными структурами объектов.
  • Объединяет родственные операции в одном классе.
  • Посетитель может накапливать состояние при обходе структуры элементов.

Недостатки:

  • Паттерн не оправдан, если иерархия элементов часто меняется.
  • Может привести к нарушению инкапсуляции элементов.
Связи с другими паттернами
  • Посетитель можно рассматривать как расширенный аналог Команды, который способен работать сразу с несколькими видами получателей.
  • Вы можете выполнить какое-то действие над всем деревом Компоновщика при помощи Посетителя.
  • Посетитель можно использовать совместно с Итератором. Итератор будет отвечать за обход структуры данных, а Посетитель – за выполнение действий над каждым её компонентом.

⏏ В начало


Дополнительно

Сущность-Атрибут-Значение (EAV)

▶ code

Шаблон Сущность-Атрибут-Значение используется для реализации модели EAV на PHP

Модель Сущность-Атрибут-Значение (EAV) - это модель данных, предназначенная для описания сущностей, в которых количество атрибутов (свойств, параметров), характеризующих их, потенциально огромно, но то количество, которое реально будет использоваться в конкретной сущности, относительно мало.

Использование:

Преимущества:

Недостатки:

⏏ В начало

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages