October 8

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

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

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

К ним относятся:

  • Chain of Responsibility (Цепочка обязанностей)
    Позволяет передавать запросы по цепочке объектов, где каждый объект принимает решение, обработать запрос или передать его дальше.
  • Command (Команда)
    Инкапсулирует запросы как объекты, позволяя параметризировать методы и ставить задачи в очередь или откатывать действия.
  • Interpreter (Интерпретатор)
    Описывает грамматику для языка и предоставляет интерпретатор, который использует эту грамматику для выполнения предложений.
  • Iterator (Итератор)
    Предоставляет способ последовательного доступа к элементам коллекции без раскрытия её внутренней структуры.
  • Mediator (Посредник)
    Определяет объект, который управляет взаимодействием между другими объектами, уменьшая их взаимозависимость.
  • Memento (Хранитель)
    Сохраняет и восстанавливает состояние объекта, не нарушая его инкапсуляцию.
  • Observer (Наблюдатель)
    Позволяет объектам подписываться и получать уведомления о событиях, происходящих в других объектах.
  • State (Состояние)
    Позволяет объектам изменять свое поведение в зависимости от состояния.
  • Strategy (Стратегия)
    Определяет семейство алгоритмов и делает их взаимозаменяемыми, предоставляя выбор способа выполнения действий.
  • Template Method (Шаблонный метод)
    Определяет скелет алгоритма в методе, позволяя подклассам переопределять отдельные шаги алгоритма без изменения его структуры.
  • Visitor (Посетитель)
    Позволяет добавить новое поведение к объектам, не изменяя их классы, путем передачи объекта посетителю.

Почему важны паттерны поведения?

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

Паттерн Chain of Responsibility

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

Цель паттерна Chain of Responsibility — разделить обязанности между несколькими обработчиками и позволить запросу динамически определять, каким образом и кто его обработает. Это позволяет избежать жесткого связывания отправителя запроса с его получателем.

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

Цель паттерна Chain of Responsibility — передать запрос по цепочке объектов, где каждый объект в цепи может обработать запрос или передать его дальше. Это создает гибкую систему распределения задач между объектами.

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

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

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

  • Отправитель (Client). Это объект, который создает запрос и отправляет его для обработки. Он не знает, какой объект в цепочке обработает запрос.
  • Базовый обработчик (Handler). Это абстрактный класс или интерфейс, определяющий метод для обработки запроса и ссылку на следующий объект в цепочке. Каждый конкретный обработчик либо обрабатывает запрос, либо передает его дальше.
  • Конкретные обработчики (Concrete Handlers). Это классы, которые реализуют метод обработки запроса. Каждый из них либо решает задачу, либо передает запрос следующему обработчику в цепи.
  • Цепочка (Chain). Это последовательность обработчиков, которые связаны между собой, что позволяет запросу двигаться по цепочке до тех пор, пока он не будет обработан.

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

  • Гибкость в обработке запросов. Паттерн Chain of Responsibility позволяет добавлять новые обработчики или изменять последовательность обработки запросов, не нарушая логику приложения.
  • Снижение связности между компонентами. Отправитель запроса не связан напрямую с конкретными обработчиками, что делает систему более гибкой и поддерживаемой.
  • Упрощение структуры кода. В сложных системах обработка запросов может быть распределена по нескольким объектам, что упрощает понимание и поддержку системы.

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


Аналогия из реальной жизни

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

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

В этой аналогии:

  • Заявка на отпуск — это запрос.
  • Руководители — это обработчики в цепи.
  • Цепочка согласований — это цепочка обязанностей.

Реальное использование в разработке (пример)

В веб-приложениях паттерн Chain of Responsibility можно использовать для обработки запросов на сервере. Например, если приложение обрабатывает входящие HTTP-запросы, то на каждом уровне могут быть разные проверки:

  • Проверка аутентификации. Если пользователь не авторизован, запрос отклоняется.
  • Проверка прав доступа. Если пользователь не имеет нужных прав, запрос передается для отказа.
  • Проверка данных. Если данные валидны, запрос передается дальше для выполнения.

Если на каком-то этапе проверки запрос не соответствует требованиям, цепочка обработки прерывается и возвращается соответствующий ответ клиенту.

Паттерн Command

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

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

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

Цель паттерна Command — инкапсулировать запрос в виде объекта, который содержит все необходимые данные для выполнения команды, включая ссылку на объект, который должен выполнить эту команду. Это позволяет работать с запросами как с объектами: сохранять их, передавать, ставить в очередь и поддерживать отмену действий.

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

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

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

  • Команда (Command). Абстрактный класс или интерфейс, который объявляет метод для выполнения команды. В нём определяются действия, которые необходимо выполнить.
  • Конкретные команды (Concrete Command). Это классы, которые реализуют метод команды, вызывая действия на целевом объекте (получателе). Каждая команда может содержать информацию о том, какие действия нужно выполнить и на каких объектах.
  • Получатель (Receiver). Это объект, который фактически выполняет команду. Команды делегируют выполнение получателю, который знает, как выполнить конкретные действия.
  • Отправитель (Invoker). Это объект, который инициирует выполнение команды. Он хранит ссылки на команды и вызывает их выполнение при необходимости.
  • Клиент (Client). Это объект, который создает команды, назначает получателей и передает команды отправителю для выполнения.

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

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

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


Аналогия из реальной жизни

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

В этой аналогии:

  • Пульт — это отправитель команд.
  • Телевизор — это получатель команд, который выполняет действие.
  • Кнопки на пульте — это команды, которые инкапсулируют конкретные действия (включение, изменение канала и т.д.).

Реальное использование в разработке (пример)

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

Например, в текстовом редакторе:

  • Команды — копировать, вставить, удалить текст.
  • Получатель — текстовый редактор, который выполняет операции над текстом.
  • Отправитель — кнопки или сочетания клавиш, которые инициируют выполнение команд.

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

Паттерн Interpreter

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

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

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

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

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

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

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

  • Контекст (Context). Это информация, которая нужна интерпретатору для работы. Контекст содержит данные, необходимые для выполнения выражений.
  • Абстрактное выражение (Abstract Expression). Определяет интерфейс для всех узлов выражений, которые будут интерпретироваться. Обычно это интерфейс или абстрактный класс, который содержит метод интерпретации.
  • Конкретные выражения (Terminal Expression). Это классы, которые реализуют интерфейс абстрактного выражения для каждой терминальной части грамматики (константы, операторы и т. д.).
  • Нетерминальные выражения (Nonterminal Expression). Эти классы описывают более сложные выражения, состоящие из нескольких терминальных выражений. Обычно они комбинируют и обрабатывают несколько подвыражений.
  • Клиент (Client). Отправитель, который использует интерпретатор для выполнения команд или выражений.

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

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

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


Аналогия из реальной жизни

Представьте, что вы пользуетесь калькулятором для выполнения математических операций. Вводимые выражения, такие как 2 + 3 * 5, должны быть правильно интерпретированы. Калькулятор сначала выполняет умножение, а затем сложение, следуя правилам приоритета операторов.

В этой аналогии:

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

Реальное использование в разработке (пример)

Представьте, что у вас есть система, которая обрабатывает поисковые запросы с использованием логических операторов, таких как AND, OR, и фильтров, таких как Category = 'Books' AND Price < 100.

Система должна уметь интерпретировать такие запросы и преобразовать их в правильные SQL-запросы или фильтры для базы данных.

В этом примере:

  • Запрос — это выражение, которое нужно интерпретировать.
  • Логические операторы и условия — терминальные выражения.
  • Интерпретатор выполняет преобразование выражения запроса в нужный SQL-запрос или операцию фильтрации.

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

Паттерн Iterator

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

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

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

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

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

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

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

  • Итератор (Iterator). Это интерфейс, который определяет методы для доступа к элементам коллекции и перемещения по ним. Обычно включает методы, такие как Next(), HasNext(), и т.д.
  • Конкретный итератор (Concrete Iterator). Реализует интерфейс Итератора для конкретной коллекции. Он хранит текущее положение в коллекции и предоставляет методы для обхода.
  • Коллекция (Aggregate). Это интерфейс или абстрактный класс, который определяет метод для создания итератора. Коллекция не отвечает за логику обхода — это задача итератора.
  • Конкретная коллекция (Concrete Aggregate). Реализует интерфейс коллекции и предоставляет итератор для своей структуры данных.
  • Клиент (Client). Это объект, который использует итератор для работы с коллекцией. Клиент может не знать, как устроена коллекция внутри, используя итератор для доступа к её элементам.

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

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

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


Аналогия из реальной жизни

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

  • Книга — это коллекция.
  • Читатель — это клиент, который использует итератор.
  • Итератор — это процесс перелистывания страниц по одной.

Реальное использование в разработке (пример)

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

Пример:

  1. Массив. Итератор будет перемещаться от первого элемента к последнему.
  2. Связанный список. Итератор перемещается по узлам списка.
  3. Дерево. Итератор может обойти дерево в порядке обхода (прямой, обратный, симметричный и т.д.).

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

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

Паттерн Mediator

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

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

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

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

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

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

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

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

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

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

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


Аналогия из реальной жизни

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

В этой аналогии:

  • Самолёты — это объекты (компоненты), которые нуждаются в координации.
  • Диспетчер — это посредник, который управляет всеми их взаимодействиями.

Реальное использование в разработке (пример)

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

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

Паттерн Memento

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

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

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

Цель паттерна Memento — предоставить способ сохранения и восстановления внутреннего состояния объекта без раскрытия его деталей. Это необходимо для поддержки функциональности "откат назад" или сохранения истории изменений.

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

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

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

  • Создатель (Originator). Это объект, чье состояние нужно сохранить или восстановить. Он создает и использует объекты Memento для сохранения и восстановления своего состояния.
  • Хранитель (Memento). Этот объект сохраняет состояние, которое было передано ему создателем. Хранитель не раскрывает состояние другим объектам и используется только создателем для восстановления состояния.
  • Опекун (Caretaker). Этот объект отвечает за хранение и управление объектами Memento, не изменяя их содержимое. Он не имеет доступа к состоянию Memento, но знает, когда и как использовать его для восстановления состояния создателя.

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

  • Сохранение инкапсуляции. Паттерн Memento позволяет сохранять внутреннее состояние объекта без нарушения инкапсуляции. Только создатель имеет доступ к деталям своего состояния.
  • Реализация механизма отмены. Хранитель позволяет реализовать функцию "Отменить" в приложении, что делает интерфейс более удобным для пользователей, позволяя им откатить ошибочные действия.
  • Гибкость восстановления состояния. Используя Memento, можно сохранять состояние в определённые моменты времени и восстанавливать его по мере необходимости, создавая гибкие системы управления состоянием.

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


Аналогия из реальной жизни

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

  • Игровое сохранение — это аналог объекта Memento, который хранит текущее состояние игры (здоровье персонажа, прогресс и т.д.).
  • Игра — это объект Originator, который сохраняет и восстанавливает свое состояние.
  • Игрок — это Caretaker, который решает, когда сохранить и когда загрузить состояние.

Реальное использование в разработке (пример)

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

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

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

Паттерн Observer

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

Цель паттерна Observer — организовать уведомления для группы зависимых объектов, когда изменяется состояние одного объекта, при этом минимизируя взаимозависимость между объектами.

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

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

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

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

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

  • Наблюдаемый объект (Subject). Это объект, за состоянием которого следят. Он поддерживает список наблюдателей и содержит методы для добавления, удаления и уведомления наблюдателей.
  • Наблюдатель (Observer). Это интерфейс или абстрактный класс, который определяет метод для получения уведомлений об изменении состояния наблюдаемого объекта.
  • Конкретный наблюдатель (Concrete Observer). Это класс, реализующий интерфейс наблюдателя и реагирующий на изменения состояния наблюдаемого объекта.
  • Конкретный наблюдаемый объект (Concrete Subject). Это класс, который отслеживает своё состояние и уведомляет всех подписчиков при изменении этого состояния.

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

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

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


Аналогия из реальной жизни

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

  • Веб-сайт — это наблюдаемый объект (Subject), который следит за обновлением контента.
  • Подписчик — это наблюдатель (Observer), который получает уведомления об обновлениях.
  • Рассылка уведомлений — это процесс оповещения всех подписчиков о новых статьях или новостях.

В этой аналогии:

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

Реальное использование в разработке (пример)

В приложениях паттерн Observer может использоваться для реализации механизма подписки на события. Например, в пользовательском интерфейсе (UI) объект может представлять собой форму, и когда пользователь вносит изменения в форму, все компоненты, подписанные на эту форму, должны быть уведомлены о её изменениях:

  1. Форма (Subject) — это наблюдаемый объект, который уведомляет подписчиков при изменении данных.
  2. Элементы UI (Observers) — это наблюдатели, которые обновляют свое состояние в зависимости от изменений данных в форме.
  3. Механизм уведомления — каждый раз, когда данные формы изменяются, все подписанные компоненты обновляются автоматически.

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

Паттерн State

Паттерн State (Состояние) относится к поведенческим паттернам проектирования и решает задачу изменения поведения объекта в зависимости от его текущего состояния. Вместо использования многочисленных условных операторов, таких как if или switch, объект делегирует выполнение своих действий объекту-состоянию, который отвечает за конкретное поведение. Паттерн применяется, когда у объекта есть несколько состояний, каждое из которых изменяет его поведение.

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

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

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

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

  • Сложная логика состояний. В системах, где объекты могут находиться в разных состояниях и изменять поведение в зависимости от этих состояний, условные операторы (например, if-else или switch) могут сильно усложнить код. Паттерн State помогает разделить состояния на независимые классы, каждый из которых управляет конкретным поведением.
  • Частые изменения состояния. В системах, где объекты часто изменяют свое состояние, этот паттерн помогает избежать дублирования кода и делает систему более гибкой для изменений.
  • Гибкость и расширяемость. При добавлении новых состояний не нужно изменять основной код объекта. Достаточно добавить новый класс, который реализует поведение для нового состояния.

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

  • Контекст (Context). Это объект, который управляет своим текущим состоянием и делегирует выполнение поведения объектам-состояниям. Контекст содержит ссылку на текущее состояние и может изменять её при переходе в другое состояние.
  • Состояние (State). Это абстрактный класс или интерфейс, определяющий методы, которые должны быть реализованы для каждого конкретного состояния.
  • Конкретные состояния (Concrete States). Это классы, которые реализуют поведение для каждого отдельного состояния. В зависимости от состояния они могут изменять текущее состояние контекста.

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

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

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


Аналогия из реальной жизни

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

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

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

В этой аналогии:

  • Автомат — это контекст.
  • Ожидание монеты, прием монеты, выбор напитка, выдача напитка — это конкретные состояния.
  • Изменение состояний автомата — это изменения его поведения в зависимости от текущего состояния.

Реальное использование в разработке (пример)

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

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

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

Паттерн Strategy

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

Цель паттерна Strategy — отделить выбор алгоритма от его реализации, чтобы клиент мог динамически переключаться между различными вариантами поведения в зависимости от ситуации.

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

Цель паттерна Strategy — предоставить объектам возможность выбирать один из нескольких алгоритмов для выполнения задачи без изменения самого клиента. Это делает код более гибким и поддерживаемым, так как новые алгоритмы могут быть добавлены или изменены независимо от основной логики приложения.

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

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

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

  • Контекст (Context). Это класс, который использует стратегию для выполнения некоторой операции. Он не знает конкретных деталей реализации стратегии и лишь вызывает метод для выполнения задачи.
  • Стратегия (Strategy). Это интерфейс, который определяет общий метод для всех алгоритмов. Все конкретные стратегии должны реализовать этот интерфейс, предоставляя свою собственную реализацию метода.
  • Конкретные стратегии (Concrete Strategies). Это классы, которые реализуют различные алгоритмы и предоставляют конкретную логику выполнения метода, определенного в интерфейсе стратегии.

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

  • Гибкость выбора алгоритмов. Паттерн Strategy позволяет легко менять алгоритмы для выполнения задачи без изменения исходного кода клиента.
  • Снижение дублирования кода. Алгоритмы изолированы друг от друга, что упрощает их тестирование, поддержку и повторное использование.
  • Поддерживаемость. Добавление новых алгоритмов не требует изменения существующих классов. Просто создается новая стратегия, которая интегрируется в контекст.

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


Аналогия из реальной жизни

Представьте, что вам нужно рассчитать стоимость доставки товара. В зависимости от веса и расстояния вы можете выбрать разные варианты доставки:

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

В этой аналогии:

  • Методы доставки — это стратегии.
  • Расчет стоимости доставки — это алгоритмы.
  • Клиент — это система, которая выбирает подходящий метод доставки на основе входных данных.

Реальное использование в разработке (пример)

В приложениях паттерн Strategy можно использовать для обработки разных форматов оплаты в электронной коммерции. Например:

  • Оплата через кредитную карту.
  • Оплата через PayPal.
  • Оплата через криптовалюту.

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

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

Паттерн Template Method

Паттерн Template Method (Шаблонный метод) относится к поведенческим паттернам проектирования и решает задачу определения "скелета" алгоритма, оставляя детализацию отдельных шагов на усмотрение подклассов. Этот паттерн позволяет подклассам переопределять конкретные этапы алгоритма без изменения его общей структуры.

Цель паттерна Template Method — зафиксировать основную последовательность шагов алгоритма в базовом классе, позволяя наследникам переопределять конкретные шаги, что обеспечивает гибкость и повторное использование кода.

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

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

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

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

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

  • Абстрактный класс (AbstractClass). Определяет шаблон метода, который содержит основной алгоритм. Шаблонный метод включает последовательность шагов, некоторые из которых реализуются в базовом классе, а другие объявляются как абстрактные и должны быть реализованы в подклассах.
  • Конкретные классы (ConcreteClass). Наследуют абстрактный класс и реализуют абстрактные шаги алгоритма. Эти классы могут изменять только определенные шаги алгоритма, не затрагивая его основную структуру.
  • Шаблонный метод (Template Method). Это метод, который определяет последовательность шагов алгоритма. Некоторые шаги могут быть реализованы в базовом классе, а другие — переопределены в подклассах.

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

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

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


Аналогия из реальной жизни

Представьте процесс приготовления кофе в кофемашине:

  1. Включить кофемашину.
  2. Засыпать кофе.
  3. Налить воду.
  4. Нажать кнопку запуска.

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

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

Реальное использование в разработке (пример)

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

  1. Открытие документа.
  2. Чтение данных.
  3. Сохранение изменений.

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

Паттерн Visitor

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

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

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

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

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

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

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

  • Элемент (Element). Это интерфейс или абстрактный класс, который должен иметь метод Accept, принимающий посетителя в качестве параметра. Каждый класс-элемент реализует этот метод, чтобы позволить посетителю выполнить операцию над ним.
  • Конкретные элементы (Concrete Elements). Это классы, которые реализуют метод Accept и принимают посетителя. Каждый конкретный элемент вызывает соответствующий метод посетителя для своей обработки.
  • Посетитель (Visitor). Это интерфейс, который определяет методы для посещения каждого конкретного элемента. Каждый метод посетителя соответствует определенному классу элемента.
  • Конкретные посетители (Concrete Visitors). Это классы, реализующие интерфейс посетителя. Они содержат реализацию операций, которые выполняются над конкретными элементами.

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

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

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


Аналогия из реальной жизни

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

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

В этой аналогии:

  • Экспонаты ничего не делают самостоятельно, они просто принимают посетителя (гида).
  • Гид знает, как обработать каждый экспонат, объясняя его историю или значение.

Реальное использование в разработке (пример)

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

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

  1. Каждый тип узла дерева (например, выражение, оператор, переменная) — это элемент, который реализует метод Accept.
  2. Разные посетители могут быть ответственны за различные операции: один посетитель выполняет синтаксический анализ, другой — оптимизацию, третий — генерацию кода.

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