❗ Удалите из кода If-Else и Switch Case
Расширение репертуара подходов и методов для устранения ветвления – один из быстрых способов улучшить проект. Рассказываем, как вы можете сделать свой код чище и приятнее.
Перевод публикуется с сокращениями, автор оригинальной статьи Nicklas Millard.
Cкорее всего, if-else
и switch
– ваш обычный
подход к ветвлению кода, но в нем нет необходимости. Вы можете полностью
исключить ключевое слово else
из своего словаря по программированию.
Некоторые матерые кодеры говорят, что if-else
– полиморфизм новичков.
Что плохого в традиционном ветвлении?
Много чего. Традиционное ветвление быстро разрастается. Вам придется изменять существующий код каждый раз при добавлении новой функции. Это нарушает принцип Open-Closed. Функции должны быть реализованы с помощью новых классов.
Какие есть альтернативы?
Существует масса альтернатив, но мы рассмотрим три типовых подхода, которые часто применяются при удалении традиционного ветвления из кода:
- концепции моделей с классами;
- использование полиморфного выполнения при работе с изменяющимися состояниями объектов;
- инкапсуляция стратегии в отдельные классы.
Эти 3 подхода легко справятся с большинством повседневных ситуаций, с которыми вы можете столкнуться.
Все методы имеют общие черты:
- Новая функциональность реализуется с помощью новых классов. Добавление кода, а не его изменение, обычно является более безопасным вариантом. Каждый раз, когда изменяется уже используемый код, вы подвергаетесь огромному риску все сломать.
- Проще тестировать специализированные классы – это огромное преимущество. Простые классы и методы легче рассматривать – легче понять множество небольших и сплоченных классов, чем несколько монолитных.
- Управление концепциями на низком уровне лучше, чем расширение обязанностей одного класса.
Моделирование концепций с помощью простых классов
Допустим, у нас есть
класс User
и в нем имя пользователя. Оно является строкой и имеет два условия:
не может быть нулем или пустой строкой и не может превышать 50 символов.
Можно с уверенностью предположить, что нам понадобятся имена пользователей в других частях приложения. Каждый раз, когда мы получаем имя, придется выполнять одни и те же проверки, которые будут разбросаны по всему проекту.
Если что-то изменится и возникнет необходимость в валидации специальных символов, таких как «æøå», придется найти каждое место, где получено имя пользователя, и добавить новую проверку.
Гораздо лучшим подходом является перенос концепции имени пользователя и создание небольшого специализированного объекта, как показано ниже.
Этот фрагмент кода, несомненно, чище. Теперь каждый раз, когда потребуется обновление, его нужно будет делать только в одном месте.
Изменение реализации метода объекта в зависимости от его состояния
Иногда необходимо, чтобы объект вел себя по-разному в зависимости от его внутреннего состояния. Типичный ленивый способ реализации этого – традиционное ветвление, как в приведенном ниже примере.
Всякий раз, когда нам нужно вывести деньги, в аккаунте следует проверить внутреннее состояние и вести себя соответственно.
Паттерн состояния – это очень крутой подход к удалению сумасшедшей вложенной условной логики. Пытайтесь добиться, чтобы наш код был похож на следующий:
Видите, какая плоская конструкция?
Теперь каждая ветвь инкапсулирована
в собственный класс, а класс account
делегирует ответственность
специализированному AccountState
.
Удвойте количество кода и повысьте читабельность.
У нас есть базовый класс,
от которого наследуется каждый объект состояния. Если мы получим новый запрос на добавление состояния RequiresValidation
или что-то еще, будет легко реализовать эту функцию, не касаясь существующих классов.
Внимательный читатель заметил, что нет никакого перехода в состояние. Очевидно, что классы не полностью реализованы, т. к. переход из одного состояния в другое выходит за рамки статьи.
Рефакторинг ветвлений на отдельные классы
Наиболее часто используемый способ устранения условных ветвлений – объекты стратегии.
Допустим, мы хотим
преобразовать любой тип в формат CSV и указать, как преобразуется
каждое свойство типа, но тип не должен определять это сам. [Csv Info]
– это атрибут, который по сути является метаинформацией о типе. Затем эта метаинформация считывается с помощью крошечной рефлексии внутри метода ToCsv()
.
Ниже приведен фрагмент класса
CsvInfoAttribute
. Этот код не является полным мусором, но он не очень
расширяемый и не очень гибкий. Каждый раз, когда нужно будет добавить новый параметр преобразования, придется добавлять и дополнительный enum, а затем реализовывать преобразование для него в методе Format()
. Это означает, что вам нужно изменить существующий код.
Предположим, что класс живет в какой-то общей библиотеке, от которой зависит много проектов в вашей организации. Если одной команде нужен новый вариант преобразования, ей придется обратиться к сопровождающим проекта, чтобы они добавили функциональность, создали новую версию и опубликовали ее.
Мы можем сделать это,
разделив каждую ветку switch/case
на специализированные классы, что делает enum
ненужным.
Каждая стратегия должна реализовывать
общий интерфейс, а CsvInfoAttribute
больше не должен иметь собственного метода Format()
.
Вместо этого он делегирует ответственность за форматирование специализированным
объектам.
Рассмотрим приведенный ниже код:
Чтобы это сработало, необходимо
определить некоторые ограничения: любой форматер должен реализовать
IValueFormatter
и иметь конструктор по умолчанию.
Представьте, как легко сейчас протестировать и исправить ошибки. Вы всегда будете точно знать, что проверять и где искать. Больше никаких следов безумной switch-логики.
Заключение
К сожалению, традиционное ветвление используется очень широко. Многие разработчики придерживаются своего испытанного и верного способа ветвления кода, не осознавая, что обслуживание и добавление функциональности превратилось в кошмар.
Если вы действительно хотите стать сильным разработчиком, начните искать способы устранения традиционного ветвления или используйте описанные в статье.
Дополнительные материалы:
- Прощай, плохой код: вот как не лажать в JavaScript
- Как писать код каждый день на протяжении года
- Как написать код, который полюбят все
- Почему нельзя просто взять и перейти на Python 3
- Всегда чистый код: 5 инструментов для JavaScript-разработчика