О поведенческих шаблонах проектирования простым языком

Posted by

Рассказывает Камран Ахмед

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

Википедия описывает их следующим образом:

Будьте осторожны

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

Также заметьте, что примеры ниже написаны на PHP 7. Но это не должно вас останавливать, ведь принципы остаются такими же.

Типы шаблонов

Шаблоны бывают следующих трех видов:

— Порождающие.
— Структурные.
— Поведенческие — о них мы рассказываем в этой статье.

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

Википедия гласит:

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

цепочка обязанностей (Chain of Responsibility);
команда (Command);
итератор (Iterator);
посредник (Mediator);
хранитель (Memento);
наблюдатель (Observer);
посетитель (Visitor);
стратегия (Strategy);
состояние (State);
шаблонный метод (Template Method).

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

Википедия гласит:

Пример из жизни: например, у вас есть три платежных метода (A, B и C), настроенных на вашем банковском счёте. На каждом лежит разное количество денег. На A есть 100 долларов, на B есть 300 долларов и на C — 1000 долларов. Предпочтение отдается в следующем порядке: A, B и C. Вы пытаетесь заказать что-то, что стоит 210 долларов. Используя цепочку обязанностей, первым на возможность оплаты будет проверен метод А, и в случае успеха пройдет оплата и цепь разорвется. Если нет, то запрос перейдет к методу B для аналогичной проверки. Здесь A, B и C — это звенья цепи, а все явление — цепочка обязанностей.

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

Обратимся к коду. Приведем пример с банковскими счетами. Изначально у нас есть базовый Account с логикой для соединения счетов цепью и некоторые счета:

Теперь приготовим цепь, используя объявленные выше звенья (например, Bank, Paypal, Bitcoin):

Примеры на Java и Python.

Команда (Command)

Википедия гласит:

Пример из жизни: Типичный пример: вы заказываете еду в ресторане. Вы (т.е. Client) просите официанта (например, Invoker) принести еду (то есть Command), а официант просто переправляет запрос шеф-повару (то есть Receiver), который знает, что и как готовить. Другим примером может быть то, что вы (Client) включаете (Command) телевизор (Receiver) с помощью пульта дистанционного управления (Invoker).

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

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

Затем у нас есть интерфейс Command, который каждая команда должна реализовывать, и затем у нас будет набор команд:

Затем у нас есть Invoker, с которым клиент будет взаимодействовать для обработки любых команд:

Наконец, мы можем увидеть, как использовать нашего клиента:

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

Примеры на Java и Python.

Итератор (Iterator)

Википедия гласит:

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

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

Обратимся к примерам в коде. В PHP очень просто реализовать это, используя SPL (Standard PHP Library). Приводя наш пример с радиостанциями, изначально у нас есть Radiostation:

Затем у нас есть итератор:

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

Примеры на Java и Python.

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

Википедия гласит:

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

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

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

Изначально у нас есть посредник ChatRoomMediator:

Затем у нас есть наши User (коллеги):

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

Примеры на Java и Python.

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

Википедия гласит:

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

Простыми словами: Шаблон хранитель фиксирует и хранит текущее состояние объекта, чтобы оно легко восстанавливалось.

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

Изначально у нас есть наш объект EditorMemento, который может содержать состояние редактора:

Затем у нас есть наш Editor (создатель), который будет использовать объект хранитель:

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

Примеры на Java и Python.

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

Википедия гласит:

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

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

Обратимся к коду. Приводя наш пример. Изначально у нас есть JobSeeker, которые ищут работы JobPost и должны быть уведомлены о её появлении:

Затем мы делаем публикации JobPostings на которые соискатели могут подписываться:

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

Примеры на Java и Python.

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

Википедия гласит:

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

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

Перейдем к примерам в коде. Возьмём зоопарк: у нас есть несколько видов Animal, и нам нужно послушать издаваемые ими звуки.

Затем у нас есть реализация для животных:

Давайте реализуем посетителя:

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

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

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

Примеры на Java и Python.

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

Википедия гласит:

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

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

Перейдем к коду. Возьмем наш пример. Изначально у нас есть наша SortStrategy и разные её реализации:

И у нас есть Sorter, который собирается использовать какую-то стратегию:

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

Примеры на Java и Python.

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

Википедия гласит:

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

Простыми словами: Шаблон позволяет менять поведение класса при изменении состояния.

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

Изначально у нас есть интерфейс WritingState и несколько его реализаций:

Затем TextEditor:

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

Примеры на Java и Python.

Паттерн состояние применяется в проектировании распределённых сиситем, наряду с другими паттернами.

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

Википедия гласит:

Пример из жизни: Допустим, вы собрались строить дома. Этапы будут такими:

— Подготовка фундамента.
— Возведение стен.
— Настил крыши.
— Настил перекрытий.

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

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

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

Изначально у нас есть наш Builder, который описывает скелет для построения алгоритма:

Затем у нас есть его реализации:

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

Примеры на Java и Python.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *