Не откладывая в долгий ящик скажу: зачастую конструкция if-else — плохой выбор. Её использование приводит к сложным конструкциям, снижает читаемость кода и усложняет рефакторинг.
Тем не менее, конструкция if-else де-факто стала решением для ветвления кода, что имеет смысл. Это одна из первых тем, которую изучают начинающие разработчики. К несчастью, многие так никогда и не переходят на более подходящие стратегии ветвления.
Некоторые живут по правилу: if-else молоток, всё остальное — гвозди.
Неспособность определить, когда использовать более подходящий инструмент, относится к числу признаков, отличающих начинающих и опытных разработчиков.
Я продемонстрирую несколько приёмов и шаблонов, которые помогут положить конец этой неприятной практике. Сложность будет возрастать с каждым примером.
1. Совсем лишние блоки else
Это самая распространённая ошибка начинающих разработчиков. Ниже яркий пример того, как вы проигрываете, используя if-else:
Простой if-else
Выражение легко упростить, просто убрав блок else:
Без блока else
Выглядит более профессионально, не так ли?
Вы регулярно будете обнаруживать, что блок else вам совсем не нужен, как в примере выше, когда вам нужно сделать что-то при выполнении некоторого условия и немедленно получить результат.
2. Присвоение значений
Если вам нужно присвоить значение переменной на основе некоторого ввода, бросьте эту возню с if-else — существует намного более читаемый подход:
Присвоение значения с использованием if-else
Выглядит ужасно, даже несмотря на простоту. Прежде всего, if-else легко заменяется оператором выбора. Но можно ещё упростить код, удалив else if и else.
Оператор if с быстрым return
Убираем 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— внедрение процесса обнаружения динамического типа, а в данном случае, стратегической модели.
Рефакторинг этого беспорядочного куска кода выглядит так:
Но динамическое расширение приложений — сложная тема.
В идеале обнаружение типов и словарь предоставляются снаружи метода PrintOrder. Но в любом случае давайте пробежимся по коду выше:
Тем не менее, конструкция if-else де-факто стала решением для ветвления кода, что имеет смысл. Это одна из первых тем, которую изучают начинающие разработчики. К несчастью, многие так никогда и не переходят на более подходящие стратегии ветвления.
Некоторые живут по правилу: if-else молоток, всё остальное — гвозди.
Неспособность определить, когда использовать более подходящий инструмент, относится к числу признаков, отличающих начинающих и опытных разработчиков.
Я продемонстрирую несколько приёмов и шаблонов, которые помогут положить конец этой неприятной практике. Сложность будет возрастать с каждым примером.
1. Совсем лишние блоки else
Это самая распространённая ошибка начинающих разработчиков. Ниже яркий пример того, как вы проигрываете, используя if-else:
Простой if-else
Выражение легко упростить, просто убрав блок else:
Без блока else
Выглядит более профессионально, не так ли?
Вы регулярно будете обнаруживать, что блок else вам совсем не нужен, как в примере выше, когда вам нужно сделать что-то при выполнении некоторого условия и немедленно получить результат.
2. Присвоение значений
Если вам нужно присвоить значение переменной на основе некоторого ввода, бросьте эту возню с if-else — существует намного более читаемый подход:
Присвоение значения с использованием if-else
Выглядит ужасно, даже несмотря на простоту. Прежде всего, if-else легко заменяется оператором выбора. Но можно ещё упростить код, удалив else if и else.
Оператор if с быстрым return
Убираем 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— внедрение процесса обнаружения динамического типа, а в данном случае, стратегической модели.
Рефакторинг этого беспорядочного куска кода выглядит так:
- Извлекаем каждую ветвь в отдельный стратегический класс с общим интерфейсом.
- Динамически находим все классы, реализующие общий интерфейс.
- На основе входных данных решаем, какую стратегию использовать.
Но динамическое расширение приложений — сложная тема.
В идеале обнаружение типов и словарь предоставляются снаружи метода PrintOrder. Но в любом случае давайте пробежимся по коду выше:
- Подпись метода не изменилась, так как вызывающей программе не нужно знать о рефакторинге.
- Для начала получим все типы в блоке, реализующие общий интерфейс IOrderOutputStrategy.
- Затем соберём словарь, в котором displayName форматировщика является ключом, а тип — значением.
- Затем тип форматировщика выбирается из словаря, мы пытаемся создать экземпляр стратегического объекта.
- И наконец вызывается ConvertOrderToString стратегического объекта.