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

Что такое многостороннее разветвление и его недостатки?

Код многостороннего ветвления включает типичные операторы if-else или switch для принятия решений. Ниже приведен пример

Приведенный выше код получает в качестве входных данных тип мессенджера и сообщение. Далее выбирает мессенджер на основе условий для отправки сообщения. Хотя нет смысла писать такой производственный код, но все же его можно найти в кодовых базах. Вышеупомянутый класс является явным нарушением принципа единой ответственности и принципа открытого закрытия. Этот класс, безусловно, изменится в будущем по многим причинам, поскольку требования могут расти, проверки могут измениться, клиенты мессенджера могут измениться и т. Д. Что, если сообщение необходимо отправить на несколько мессенджеров? Это процедурный способ выполнения вещей, который можно улучшить, инкапсулируя логику отправки сообщения в различных классах. Конечная цель - преобразовать вышеуказанные операторы if-else в this.

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

Инкапсулировать логику отправки сообщения в нескольких реализациях

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

Теперь Messenger Client будет выглядеть так.

Реализация мессенджера будет такой

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

Инкапсулируйте логику принятия решения

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

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

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

Отправка сообщения в несколько мессенджеров

Что, если изменится бизнес-требование для отправки сообщения как в Slack, так и в Skype, если тип мессенджера - admin? Давайте воспользуемся для этого составным шаблоном проектирования. Создайте административную реализацию интерфейса Messenger.

Далее зарегистрируйте админ-мессенджер с клиентом. Теперь сообщение администратора будет отправлено и в Slack, и в Skype.

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

Тем не менее, в клиентском коде есть шум, и под ним обсуждалась еще одна проблема.

Шум генерируется синтаксисом Java, таким как поток, фильтр, findFirst и т. Д. Давайте заставим этот код говорить на языке бизнес-домена. Зоран Хорват очень хорошо объясняет эту технику в своем курсе Освоение объектно-ориентированного программирования на Java.

Сначала создайте интерфейс ForwardingStream, расширяющий интерфейс java.util.Stream, и реализуйте все методы по умолчанию.

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

Логика поиска подходящего мессенджера из списка мессенджеров инкапсулирована, а название метода говорит на языке бизнеса. Затем создайте класс OptionalMessenger.

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

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

Не позволяйте клиенту отправлять сообщение без соответствия

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

Чтобы исправить это, можно сделать разделение интерфейса. Разделите интерфейс Messenger на два интерфейса: Действие и Правило.

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

Измените OptionalMessenger, чтобы использовать интерфейс Action, как он выполняет действие по отправке сообщения.

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

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

Конечно, все вышесказанное - мое мнение и может быть улучшено или реализовано иначе. Спасибо за внимание.