Около 10 лет назад я столкнулся с анти-if кампанией и счел ее абсурдной концепцией. Как вы можете создать полезную программу без использования оператора if? Абсурдно. Но потом это заставляет задуматься. Помните тот вложенный код, который вам пришлось разбирать на прошлой неделе? Это было ужасно, верно? Если бы только был способ сделать его проще. На сайте кампании " Анти-if", к сожалению, мало практических советов. Данная статья призвана исправить это положение с помощью подборки паттернов, которые вы можете взять на вооружение, когда возникнет такая необходимость. Но сначала давайте рассмотрим проблему, которую создают операторы if. Первая проблема с операторами if заключается в том, что они часто упрощают модификацию кода не в лучшую сторону. Давайте начнем с появления нового оператора if: На данный момент все не так уж плохо, но мы уже столкнулись с некоторыми проблемами. Когда я читаю этот код, мне приходится проверять, как CodeBlockA и CodeBlockB изменяют одно и то же SharedState. Сначала это можно легко прочитать, но по мере увеличения CodeBlock'ов и возникновения сложностей с связанностью (coupling) это может стать трудной задачей. Часто можно увидеть, как вышеупомянутые блоки CodeBlocks злоупотребляют дальнейшими вложенными операторами if и локальными возвратами. Это затрудняет понимание бизнес-логики через систему маршрутизации. Вторая проблема с операторами if заключается в том, что они дублируются. Это означает, что концепция домена отсутствует. Можно легко увеличить связанность, объединяя то, что не нужно. В результате будет труднее читать и изменять код. Третья проблема с операторами if заключается в том, что вам приходится моделировать процесс выполнения в своей голове. Вы должны стать мини-компьютером. Это отнимает у вас умственную энергию, которую лучше потратить на решение проблемы, а не на то, как переплетаются между собой отдельные ветви кода. Я хочу перейти к тому, чтобы рассказать вам, какие паттерны мы можем применить вместо этого, но сначала несколько слов для предупреждения. Операторы If обычно усложняют код. Но мы не хотим полностью запрещать их. Я видел довольно отвратительный код, созданный с целью удалить все следы операторов if. Мы хотим избежать попадания в эту ловушку. Для каждого паттерна, о котором мы будем читать, я дам вам допустимое значение, когда его можно использовать. Одиночный оператор if, который больше нигде не дублируется, — это, скорее всего, нормально. А вот когда у вас есть дублирующиеся операторы if, вам следует обратить внимание на свое чутье. На внешней стороне вашей кодовой базы, где вы взаимодействуете с внешней средой, вы наверняка захотите проверить входящие ответы и соответствующим образом изменить свое поведение. Но внутри наших собственных кодовых баз, я думаю, у нас есть прекрасная возможность использовать простые, содержательные и более мощные альтернативы. Контекст: У вас есть метод, который принимает булево (Boolean) значение, изменяющее его поведение. Проблема: Каждый раз, когда вы видите это, у вас на самом деле два метода, объединенные в один. Этот булев позволяет задать имя концепции в вашем коде. Допустимые варианты: Обычно, когда вы видите этот контекст, вы можете определить во время компиляции, по какому пути пойдет код. Если это так, то всегда используйте этот паттерн. Решение: Разделите метод на два новых метода. Вуаля, if исчезло. Контекст: Вы переходите на другой тип. Проблема: Когда мы добавляем новый тип, нам нужно не забыть обновить оператор switch. Кроме того, в этом классе Bird страдает связность, поскольку добавляется несколько концепций различных Bird классов. Допустимые варианты: Одиночный switch — это нормально. Когда их несколько, могут возникнуть ошибки, поскольку человек, добавляющий новый тип, может забыть провести обновления по всем switch, которые доступны для этого скрытого типа. В блоге 8thlight есть отличная статья по этому вопросу. Решение: Используйте полиморфизм. Каждый, кто вводит новый тип, не может забыть про связанное с ним поведение. Примечание: В этом примере для краткости включен только один метод, однако более убедительно выглядит вариант, когда несколько применяется несколько switch. Контекст: Посторонний человек, которого просят понять основную цель вашей кодовой базы, отвечает: "нужно проверять, равны ли значения null". Проблема: Ваши методы должны проверять, передаются ли им ненулевые значения. Допустимость: Необходимо обеспечивать защиту во внешних частях вашей кодовой базы, но защита внутри вашей кодовой базы, вероятно, означает, что код, который вы пишете, является "оскорбительным". Не пишите "оскорбительный код". Решение: Используйте тип NullObject или Optional вместо того, чтобы проверять, передаются ли нулевые значения. Пустой набор данных — отличная альтернатива. Контекст: У вас есть дерево оператора if, которое вычисляет булево выражение. Проблема: Этот код заставляет вас использовать свой мозг, чтобы смоделировать, как компьютер будет проходить через ваш метод. Допустимые варианты: Очень мало. Такой код легче читать в одну строку. Или разбить на разные части. Решение: Упростите операторы if до одного выражения. Контекст: Вы вызываете какой-то другой код, но не уверены, что удастся вам успешной пройти этот путь. Проблема: Подобные операторы if множатся каждый раз, когда вы имеете дело с одним и тем же объектом или структурой данных. Они имеют скрытую связь, где "null" что-то означает. Другие объекты могут возвращать другие "магические значения", которые означают отсутствие результата. Допустимые варианты: Лучше поместить этот оператор if в одно место, чтобы он не дублировался, и устранить связь с "магическим значением" пустого объекта. Решение: Предоставьте вызываемому коду стратегию решения проблем. Hash#fetch в Ruby — хороший пример, того, как справился Java. Этот паттерн можно развивать и дальше, чтобы устранить исключения. Надеюсь, вы сможете использовать некоторые из этих паттернов в коде, над которым вы сейчас работаете. Я нахожу их полезными при рефакторинге кода, чтобы лучше понять его. Помните, что операторы if — это далеко не все зло. В современных языках у нас есть богатый набор возможностей, которыми мы должны воспользоваться. Revealing the design of FizzBuzz Four qualities that underpin an effective test suite Choose one: Feature Toggles vs Authorisation Материал подготовлен в рамках курса «Архитектура и шаблоны проектирования». Если вам интересно узнать подробнее о формате обучения и программе, познакомиться с преподавателем курса — приглашаем на день открытых дверей онлайн. Регистрация здесь. Редактор