Не откладывая в долгий ящик скажу: зачастую конструкция if-else — плохой выбор. Её использование приводит к сложным конструкциям, снижает читаемость кода и усложняет рефакторинг.
Тем не менее, конструкция if-else де-факто стала решением для ветвления кода, что имеет смысл. Это одна из первых тем, которую изучают начинающие разработчики. К несчастью, многие так никогда и не переходят на более подходящие стратегии ветвления.
Некоторые живут по правилу: if-else молоток, всё остальное — гвозди.
Неспособность определить, когда использовать более подходящий инструмент, относится к числу признаков, отличающих начинающих и опытных разработчиков.
Я продемонстрирую несколько приёмов и шаблонов, которые помогут положить конец этой неприятной практике. Сложность будет возрастать с каждым примером.
1. Совсем лишние блоки else
Это самая распространённая ошибка начинающих разработчиков. Ниже яркий пример того, как вы проигрываете, используя if-else:
Выражение легко упростить, просто убрав блок else
:
Выглядит более профессионально, не так ли?
Вы регулярно будете обнаруживать, что блок else вам совсем не нужен, как в примере выше, когда вам нужно сделать что-то при выполнении некоторого условия и немедленно получить результат.
2. Присвоение значений
Если вам нужно присвоить значение переменной на основе некоторого ввода, бросьте эту возню с if-else — существует намного более читаемый подход:
Выглядит ужасно, даже несмотря на простоту. Прежде всего, if-else легко заменяется оператором выбора. Но можно ещё упростить код, удалив else if
и else
.
Убираем else if
и else
и получаем чистый читаемый код. Обратите внимание, что я изменил стиль быстрого возвращаемого значения — просто не имеет смысла проверять значение, когда верное уже получено.
3. Проверка входных условий
Чаще всего я обнаруживаю, что нет смысла продолжать выполнение метода, если он включает неверные значения.
Скажем, у нас есть вышеописанный метод DetermineGender с требованием, чтобы входное значение равнялось 0 или 1:
Выполнение метода без проверки значений не имеет смысла. Следовательно, нам нужно проверить входные условия перед выполнением метода.
Применив граничные операторы метода безопасного программирования, сначала проверяем входные значения и только потом выполняем метод if:
На этом этапе мы убедились, что основная логика выполняется, только если значения находятся в ожидаемом диапазоне.
Операторы if также заменены на тернарные, так как больше нет смысла возвращать значение “Unknown”.
4. Превращение if-else в словарь — полностью избегаем if-else
Скажем, нужно выполнить некоторую операцию, выбранную на основе некоторого условия, и заранее известно, что позднее нужно будет добавлять ещё операции:
Возможно, кто-то использует старый добрый if-else. Добавление новой операции — просто добавление ещё одного if. Всё просто. Однако с точки зрения производительности этот подход неэффективен.
Зная, что впоследствии нужно будет добавлять ещё операции, превратим if-else в словарь:
Читаемость выросла, код проще понять.
Обратите внимание, что словарь размещён внутри метода исключительно в целях демонстрации. Вам, скорее всего, захочется, чтобы он был предоставлен извне.
5. Расширение приложений — полностью избегаем if-else
Немного более продвинутый пример
Необходимо уточнить — это более “корпоративный” подход. Нетипичный сценарий “давайте заменим if-else”. Теперь можно продолжить.
If можно полностью заменить объектами.
Довольно часто необходимо расширить какую-то часть приложения. Как начинающий разработчик вы можете просто использовать дополнительный оператор if-else (или else-if).
Рассмотрим пример: нам нужно представить экземпляр Order в виде строки. Во-первых, у нас есть только два типа представления строки — JSON и простой текст. Использование if-else на этом этапе не является проблемой, но мы с лёгкостью можем заменить else if
на if
, как было показано выше.
Однако этот подход определённо не приемлем, если эту часть приложения нужно будет расширять.
Мало того, что код выше не соответствует принципу открытости/закрытости, его трудно читать и поддерживать в долгосрочной перспективе.
Правильный подход — подход, придерживающийся принципов SOLID— внедрение процесса обнаружения динамического типа, а в данном случае, стратегической модели.
Рефакторинг этого беспорядочного куска кода выглядит так:
- Извлекаем каждую ветвь в отдельный стратегический класс с общим интерфейсом.
- Динамически находим все классы, реализующие общий интерфейс.
- На основе входных данных решаем, какую стратегию использовать.
Посмотрите на код, заменяющий пример выше. Да, здесь значительно больше кода, требуется знать, как работает обнаружение типов.
Но динамическое расширение приложений — сложная тема.
Я продемонстрировал только часть, заменяющую пример с if-else. Здесь можно увидеть все вовлечённые объекты.
В идеале обнаружение типов и словарь предоставляются снаружи метода PrintOrder. Но в любом случае давайте пробежимся по коду выше:
- Подпись метода не изменилась, так как вызывающей программе не нужно знать о рефакторинге.
- Для начала получим все типы в блоке, реализующие общий интерфейс
IOrderOutputStrategy
. - Затем соберём словарь, в котором displayName форматировщика является ключом, а тип — значением.
- Затем тип форматировщика выбирается из словаря, мы пытаемся создать экземпляр стратегического объекта.
- И наконец вызывается
ConvertOrderToString
стратегического объекта.
Читайте также:
- 2 черты отличных программистов
- Искусственный интеллект + распределённые реестры = оружие против фейковых новостей
- Разоблачение мифов о сеньоре
Перевод статьи Nicklas Millard: Better Software Without If-Else