за что отвечает паттерн mediator
Использование паттерна mediator для переключения между activity
В простейшем случае для запуска Activity в Android нужно создать Intent c указанием класса вызываемого activity и Bundle из параметров. И всё хорошо, пока у нас в приложении пара экранов. Сложности начинаются тогда, когда количество экранов в нашем приложении будет исчисляться десятками. В данной статье я бы хотел предложить относительно несложный способ организации работы с большим количеством Activity.
Введение
Для того, что бы разговор был более предметным, будем считать, что в нашем приложении есть по крайней мере 3 активити:
Проблема
Что является главным недостатком стандартного подхода, при котором экземпляры Intent создаются прямо в коде Activity. Это дублирование кода. Если мы захотим добавить к вызову активити ещё один параметр, нам придётся изменить все места, где это активити вызывается. Если приложение разрабатывается более чем одним человеком, то придётся оповестить всю команду о сделанных изменениях. Если потребуется заменить одно активити другим, потребуется опять-таки искать все вхождения. И пускай такую работу за нас может выполнить IDE, все равно это не фен-шуй.
Решение
Реализация
Диаграмма классов приведена в начале статьи. Сначала рассмотрим каждый из классов подробнее.
Класс MyIntent хранит константы для параметров вызова activity.
Класс ActivityMediator реализует вывоз активити по данному классу. Конструктор принимает текущее активити и сохраняет в приватное поле. Два защищённых метода вызывают активити по данному классу без экстра или с ним. Защищёнными эти методы сделаны для того, что бы избежать соблазна вызывать их напрямую.
От класса ActivityMediator наследуем класс MyActivityMediator.
Класс MyActivity — абстрактный, от него наследуются все остальные активити в проекте.
Теперь в любом активити нашего приложения мы можем вызвать любое другое активити. Например, так можно вызвать редактор для создания нового документа:
Паттерн Mediator (посредник)
Назначение паттерна Mediator
Решаемая проблема
Мы хотим спроектировать систему с повторно используемыми компонентами, однако существующие связи между этими компонентами можно охарактеризовать феноменом «спагетти-кода».
Обсуждение паттерна Mediator
В Unix права доступа к системным ресурсам определяются тремя уровнями: владелец, группа и прочие. Группа представляет собой совокупность пользователей, обладающих некоторой функциональной принадлежностью. Каждый пользователь в системе может быть членом одной или нескольких групп, и каждая группа может иметь 0 или более пользователей, назначенных этой группе. Следующий рисунок показывает трех пользователей, являющихся членами всех трех групп.
Разбиение системы на множество объектов в общем случае повышает степень повторного использования, однако множество взаимосвязей между этими объектами, как правило, приводит к обратному эффекту. Чтобы этого не допустить, инкапсулируйте взаимодействия между объектами в объект-посредник. Действуя как центр связи, этот объект-посредник контролирует и координирует взаимодействие группы объектов. При этом объект-посредник делает взаимодействующие объекты слабо связанными, так как им больше не нужно хранить ссылки друг на друга – все взаимодействие идет через этого посредника. Расширить или изменить это взаимодействие можно через его подклассы.
Паттерн Mediator заменяет взаимодействие «все со всеми» взаимодействием «один со всеми».
Пример рационального использования паттерна Mediator – моделирование отношений между пользователями и группами операционной системы. Группа может иметь 0 или более пользователей, а пользователь может быть членом 0 или более групп. Паттерн Mediator предусматривает гибкий способ управления пользователями и группами.
Структура паттерна Mediator
UML-диаграмма классов паттерна Mediator
Коллеги (или взаимодействующие объекты) не связаны друг с другом. Каждый из них общается с посредником, который, в свою очередь, знает об остальных и управляет ими. Паттерн Mediator делает статус взаимодействия «все со всеми» «полностью объектным».
Пример паттерна Mediator
Паттерн Mediator определяет объект, управляющий набором взаимодействующих объектов. Слабая связанность достигается благодаря тому, что вместо непосредственного взаимодействия друг с другом коллеги общаются через объект-посредник. Башня управления полетами в аэропорту хорошо демонстрирует этот паттерн. Пилоты взлетающих или идущих на посадку самолетов в районе аэропорта общаются с башней вместо непосредственного общения друг с другом. Башня определяет, кто и в каком порядке будет садиться или взлетать. Важно отметить, что башня контролирует самолеты только в районе аэродрома, а не на протяжении всего полета.
Использование паттерна Mediator
Особенности паттерна Mediator
Реализация паттерна Mediator
Демонстрация паттерна Mediator
Степень повторного использования можно повысить через разбиение системы на множество объектов, однако при этом возникает множество взаимосвязей между этими объектами, которое приводит к обратному эффекту. Для исключения этой проблемы инкапсулируйте взаимодействие между объектами (коллективное поведение) в объект-посредник. Этот посредник будет управлять взаимодействием группы объектов.
FileSelectionDialog:: widgetChanged() инкапсулирует все коллективное поведение для диалогового окна (служит центром взаимодействия). Пользователь может выбрать «взаимодействие» с полем редактирования Filter, списком Directories, списком Files или полем редактирования Selection.
Результаты вывода программы:
Реализация паттерна Mediator: до и после
Объекты Node взаимодействуют друг с другом напрямую, требуется рекурсия, неудобное удаление узла, при этом первый узел удалить не возможно.
Результаты вывода программы:
После
«Посреднический» класс List упрощает все административные функции, рекурсия исключена.
Управление зависимостями, события и паттерны Observer и Mediator
Паттерны в чистом виде встречаются довольно редко и при изучении паттернов, особенно на ранних этапах, важны не столько сами паттерны, сколько понимание механизмов (тактик), с помощью которых они реализуются. В этой статье я хотел бы описать один из таких механизмов (управление зависимостями), который используется в паттернах Observer и Mediator, но который часто упускается из внимания. Если ты только начинаешь изучать паттерны, то добро пожаловать под кат.
Управление зависимостями
Начнём с утверждения: если класс A зависит от класса B, то, не меняя поведения, можно переписать код так, что класс B будет зависеть от класса A или ввести ещё один класс C таким образом, что классы A и B будут независимы, а класс C будет связывать и зависеть от классов A и B.
Один класс зависит от другого, если он обращается к его полям или методам. Поля легко переносятся из одного класса в другой, поэтому остановимся подробнее на методах. Допустим, класс B содержит метод Print, который выводит текущую дату в консоль, а класс А вызывает этот метод.
Таким образом класс A зависит от класса B. Чтобы вывернуть эту зависимость, вместо вызова метода Print напрямую, сгенерируем событие. Теперь класс А ничего не знает о классе B, а класс B может подписаться на событие класса A. Т.е. класс B будет зависеть от класса A.
Поведение кода не меняется, а в вызывающем методе меняется только порядок инициализации объектов и передача зависимостей в конструктор.
По сути, это и есть реализация паттерна Observer в C#. Класс A — это обозреваемый объект (Observable), а класс B это обозреватель (Observer). Класс A является независимым классом, который генерирует уведомления (события). Другие классы, которые в этом заинтересованы, могут подписываться на эти события и выполнять свою логику. Система становится более динамичной за счёт того, что теперь классу A не нужно знать о других реализациях. Мы можем добавлять новые реализации, которые будут подписываться на события, при этом класс A будет оставаться неизменным.
Можно пойти дальше и убрать зависимость класса B от A, добавив внешний код, который будет связывать эти два класса, т.е. подписывать один объект на события другого.
Теперь классы A и B полностью независимы, каждый выполняет свою задачу и ничего не знает о других классах. Логика по взаимодействию между объектами уходит в новый класс. Только класс C знает в ответ на какие события и при каких условиях должны вызываться методы класса B. Таким образом, класс C становится медиатором.
Итоги: Борьба со сложностью системы
Одной из важных проблем в программировании является наличие сложных систем из запутанных классов с большим количеством зависимостей (сильносвязанные системы). Управляя зависимостями, можно уменьшить связанность, упростить систему и достичь большей динамичности и гибкости.
Паттерн Observer уменьшает связанность за счёт обращения зависимостей. Он хорошо применим, когда есть несколько источников событий и много слушателей, которые добавляются динамически. Другим хорошим примером использования этого паттерна является реактивное программирование, когда изменение состояния одного объекта приводит к изменению состояния всех зависимых от него объектов и так далее.
Паттерн Mediator уменьшает связанность системы за счёт того, что все зависимости уходят в один класс медиатор, а все остальные классы становятся независимы и отвечают только за логику, которую они выполняют. Таким образом, добавление новых классов становится проще, но с каждым новым классом логика медиатора сильно усложняется. С течением времени, если медиатор продолжает бесконтрольно разрастаться, то его становится очень тяжело поддерживать.
Опасным подводным камнем при использовании паттернов Observer и Mediator является наличие циклических ссылок, когда события из одного класса, проходя по цепочки объектов, приводят к генерированию этого же события ещё раз. Эта проблема тяжело разрешима и заметно усложняет использование паттернов.
Таким образом, в разных обстоятельствах, управляя зависимостями, можно прийти к разным паттернам, иногда к их смеси, а иногда этот механизм окажется полезен без использования паттернов вовсе. Боритесь со сложностью и не плодите сущностей.
Mediator C# | Паттерн Посредник C#
Давайте рассмотрим паттерн проектирования Посредник C# — Mediator C#, для чего он нужен и какие проблемы он решает. Где можно применять данный шаблон, а где это будет излишним.
Идея паттерна Посредник (Mediator)
Паттерн (шаблон) проектирования — это продуманный способ построения исходного кода программы для решения часто возникающих в повседневном программировании проблем проектирования. Иными словами, это уже придуманное решения, для типичной задачи. При этом паттерн не готовое решение, а просто алгоритм действий, который должен привести к желаемому результату. Давайте рассмотрим один из наиболее часто используемых поведенческих паттернов — Посредник (Mediator).
Как я уже писал ранее, существует три вида паттернов проектирования:
Посредник (Mediator) — это поведенческий паттерн, который позволяет организовать работу множества слабо связанных объектов без непосредственного общения между ними. То есть, Посредник выступает промежуточным звеном между объектами, принимая и перенаправляя сообщения.
Архитектура паттерна проектирования Посредник
Логика работы Посредника
Рассмотрим основную логику работы паттерна Посредник. Давайте смоделируем процесс работы диспетчера полетов в аэропорту. Пилоты взлетающих или идущих на посадку самолетов в районе аэропорта общаются с диспетчером вместо непосредственного общения друг с другом. Диспетчер определяет, кто и в каком порядке будет садиться или взлетать. Данный механизм взаимодействия очень точно описывает логику работы паттерна Посредник.
Реализация паттерна Посредник (Mediator) на языке C#
Для начала создадим базовый интерфейс для самолета. В обычном случае я бы организовал это через интерфейс IAircraft, но так как это просто тестовый пример и мне не хочется дублировать код я организовал абстрактный класс, в котором реализовал всю логику, а конкретные реализации моделей самолетов не содержат практически никакой логики, а служат только для демонстрации.
Для удобства работы с кодом добавим перечисление, показывающее ткущее состояние самолета. Подробнее про работу с перечислениями можно прочитать в статье Отображение значения Enum в C# на русском.
Кроме того для работы аэродрома необходимы взлетно-посадочные полосы. Создадим для них класс.
Теперь самое важное. Диспетчер будет выступать посредником между самолетами и взлетно-посадочными полосами, выполняя поиск свободных полос и самолетов для взлета и посадки. То есть, не сам каждый самолет ищет свободную полосу, а он обращается к диспетчеру (посреднику) с запросом на посадку и ожидает разрешения.
Теперь нам остается только вызвать работу наших классов.
Получаем следующий результат работы приложения.
Исходный код приложения можно скачать из репозитория https://github.com/shwanoff/Mediator.
Таким образом, нам достаточно просто удалось разработать элементарную систему компьютерного моделирования работы аэропорта с использованием паттерна Посредник и асинхронного программирования.
Рекомендую также изучить статью Паттерн Адаптер (Adapter). А еще подписывайтесь на группу ВКонтакте, Telegram и YouTube-канал. Там еще больше полезного и интересного для программистов.
Посредник
Посредник — это поведенческий паттерн проектирования, который позволяет уменьшить связанность множества классов между собой, благодаря перемещению этих связей в один класс-посредник.
Предположим, что у вас есть диалог создания профиля пользователя. Он состоит из всевозможных элементов управления — текстовых полей, чекбоксов, кнопок.
Беспорядочные связи между элементами пользовательского интерфейса.
Отдельные элементы диалога должны взаимодействовать друг с другом. Так, например, чекбокс «у меня есть собака» открывает скрытое поле для ввода имени домашнего любимца, а клик по кнопке отправки запускает проверку значений всех полей формы.
Код элементов нужно трогать при изменении каждого диалога.
Прописав эту логику прямо в коде элементов управления, вы поставите крест на их повторном использовании в других местах приложения. Они станут слишком тесно связанными с элементами диалога редактирования профиля, которые не нужны в других контекстах. Поэтому вы сможете использовать либо все элементы сразу, либо ни одного.
Паттерн Посредник заставляет объекты общаться не напрямую друг с другом, а через отдельный объект-посредник, который знает, кому нужно перенаправить тот или иной запрос. Благодаря этому, компоненты системы будут зависеть только от посредника, а не от десятков других компонентов.
В нашем примере посредником мог бы стать диалог. Скорее всего, класс диалога и так знает, из каких элементов состоит, поэтому никаких новых связей добавлять в него не придётся.
Элементы интерфейса общаются через посредника.
Основные изменения произойдут внутри отдельных элементов диалога. Если раньше при получении клика от пользователя объект кнопки сам проверял значения полей диалога, то теперь его единственной обязанностью будет сообщить диалогу о том, что произошёл клик. Получив извещение, диалог выполнит все необходимые проверки полей. Таким образом, вместо нескольких зависимостей от остальных элементов кнопка получит только одну — от самого диалога.
Чтобы сделать код ещё более гибким, можно выделить общий интерфейс для всех посредников, то есть диалогов программы. Наша кнопка станет зависимой не от конкретного диалога создания пользователя, а от абстрактного, что позволит использовать её и в других диалогах.
Таким образом, посредник скрывает в себе все сложные связи и зависимости между классами отдельных компонентов программы. А чем меньше связей имеют классы, тем проще их изменять, расширять и повторно использовать.
Пилоты самолётов общаются не напрямую, а через диспетчера.
Пилоты садящихся или улетающих самолётов не общаются напрямую с другими пилотами. Вместо этого они связываются с диспетчером, который координирует действия нескольких самолётов одновременно. Без диспетчера пилотам приходилось бы все время быть начеку и следить за всеми окружающими самолётами самостоятельно, а это приводило бы к частым катастрофам в небе.
Важно понимать, что диспетчер не нужен во время всего полёта. Он задействован только в зоне аэропорта, когда нужно координировать взаимодействие многих самолётов.
Компоненты — это разнородные объекты, содержащие бизнес-логику программы. Каждый компонент хранит ссылку на объект посредника, но работает с ним только через абстрактный интерфейс посредников. Благодаря этому, компоненты можно повторно использовать в другой программе, связав их с посредником другого типа.
Посредник определяет интерфейс для обмена информацией с компонентами. Обычно хватает одного метода, чтобы оповещать посредника о событиях, произошедших в компонентах. В параметрах этого метода можно передавать детали события: ссылку на компонент, в котором оно произошло, и любые другие данные.
Конкретный посредник содержит код взаимодействия нескольких компонентов между собой. Зачастую этот объект не только хранит ссылки на все свои компоненты, но и сам их создаёт, управляя дальнейшим жизненным циклом.
Компоненты не должны общаться друг с другом напрямую. Если в компоненте происходит важное событие, он должен оповестить своего посредника, а тот сам решит — касается ли событие других компонентов, и стоит ли их оповещать. При этом компонент-отправитель не знает кто обработает его запрос, а компонент-получатель не знает кто его прислал.
В этом примере Посредник помогает избавиться от зависимостей между классами различных элементов пользовательского интерфейса: кнопками, чекбоксами и надписями.
Пример структурирования классов UI-диалогов.
По реакции на действия пользователей элементы не взаимодействуют напрямую, а всего лишь уведомляют посредника о том, что они изменились.
Посредник в виде диалога авторизации знает, как конкретные элементы должны взаимодействовать. Поэтому при получении уведомлений он может перенаправить вызов тому или иному элементу.
Когда вам сложно менять некоторые классы из-за того, что они имеют множество хаотичных связей с другими классами.
Посредник позволяет поместить все эти связи в один класс, после чего вам будет легче их отрефакторить, сделать более понятными и гибкими.
Когда вы не можете повторно использовать класс, поскольку он зависит от уймы других классов.
После применения паттерна компоненты теряют прежние связи с другими компонентами, а всё их общение происходит косвенно, через объект-посредник.
Когда вам приходится создавать множество подклассов компонентов, чтобы использовать одни и те же компоненты в разных контекстах.
Если раньше изменение отношений в одном компоненте могли повлечь за собой лавину изменений во всех остальных компонентах, то теперь вам достаточно создать подкласс посредника и поменять в нём связи между компонентами.
Найдите группу тесно переплетённых классов, отвязав которые друг от друга, можно получить некоторую пользу. Например, чтобы повторно использовать их код в другой программе.
Создайте общий интерфейс посредников и опишите в нём методы для взаимодействия с компонентами. В простейшем случае достаточно одного метода для получения оповещений от компонентов.
Этот интерфейс необходим, если вы хотите повторно использовать классы компонентов для других задач. В этом случае всё, что нужно сделать — это создать новый класс конкретного посредника.
Реализуйте этот интерфейс в классе конкретного посредника. Поместите в него поля, которые будут содержать ссылки на все объекты компонентов.
Вы можете пойти дальше и переместить код создания компонентов в класс посредника, после чего он может напоминать фабрику или фасад.
Компоненты тоже должны иметь ссылку на объект посредника. Связь между ними удобнее всего установить, подавая посредника в параметры конструктора компонентов.
Измените код компонентов так, чтобы они вызывали метод оповещения посредника, вместо методов других компонентов. С противоположной стороны, посредник должен вызывать методы нужного компонента, когда получает оповещение от компонента.
- Устраняет зависимости между компонентами, позволяя повторно их использовать. Упрощает взаимодействие между компонентами. Централизует управление в одном месте.
Цепочка обязанностей, Команда, Посредник и Наблюдатель показывают различные способы работы отправителей запросов с их получателями:
Посредник и Фасад похожи тем, что пытаются организовать работу множества существующих классов.
Разница между Посредником и Наблюдателем не всегда очевидна. Чаще всего они выступают как конкуренты, но иногда могут работать вместе.
Цель Посредника — убрать обоюдные зависимости между компонентами системы. Вместо этого они становятся зависимыми от самого посредника. С другой стороны, цель Наблюдателя — обеспечить динамическую одностороннюю связь, в которой одни объекты косвенно зависят от других.
Довольно популярна реализация Посредника при помощи Наблюдателя. При этом объект посредника будет выступать издателем, а все остальные компоненты станут подписчиками и смогут динамически следить за событиями, происходящими в посреднике. В этом случае трудно понять, чем же отличаются оба паттерна.
Но Посредник имеет и другие реализации, когда отдельные компоненты жёстко привязаны к объекту посредника. Такой код вряд ли будет напоминать Наблюдателя, но всё же останется Посредником.
Напротив, в случае реализации посредника с помощью Наблюдателя представим такую программу, в которой каждый компонент системы становится издателем. Компоненты могут подписываться друг на друга, в то же время не привязываясь к конкретным классам. Программа будет состоять из целой сети Наблюдателей, не имея центрального объекта-Посредника.
Не втыкай в транспорте
Лучше почитай нашу книгу о паттернах проектирования.
Теперь это удобно делать даже во время поездок в общественном транспорте.
Эта статья является частью нашей электронной книги Погружение в Паттерны Проектирования.