Примите неизменный и функциональный стиль кодирования

Тернарии иногда имеют плохую репутацию. Их часто описывают как трудные для чтения, обычно из-за того, что их синтаксис более абстрактен, чем прозаический if-else. Тем не менее, они показывают некоторые уникальные преимущества по сравнению с их альтернативами, как только вы пристегнетесь, чтобы ознакомиться с ними.

Если вы еще не уверены на 100%, как работает тернарный оператор, ознакомьтесь с моей статьей Тернарные операторы для начинающих. В этой статье мы рассмотрели следующий пример троичного оператора в JavaScript:

И некоторый эквивалентный код с использованием if-else:

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

Преимущества тернарного оператора

Краткость

Во-первых, троичный пример представляет собой одну строку, где эквивалент if-else составляет шесть строк, хотя это может варьироваться в зависимости от вашего предпочтительного стиля кодирования.

Синтаксис объективно более лаконичен во многих отношениях. Во-первых, в версии if-else идентификатор name повторяется трижды, а в троичной версии — только один раз. Мы также можем видеть два назначения вместо одного. Наконец, троичная версия не требует, чтобы читатель смотрел на какие-либо области видимости и отступы, в то время как версия if-else требует.

неизменность

Еще одним большим преимуществом тернарного оператора является возможность писать более неизменяемый код. Мы видим, что переменная определена и сразу же назначена в троичной версии кода. Больше он не модифицируется. Напротив, в версии if-else переменная сначала объявляется в одной строке, и только позже, в другой строке, она модифицируется, чтобы ей было присвоено правильное значение.

Поскольку переменной сразу присваивается правильное значение, мы можем использовать const, тогда как в if-else мы вынуждены использовать let. Ключевое слово const в JavaScript не позволяет нам снова переназначать переменную. Во многих других языках есть похожие ключевые слова, которые делают то же самое.

Итак, что хорошего в использовании const вместо этого? Это позволяет читателю с уверенностью предположить, что переменная никогда не будет переназначена где-либо еще в коде. Это уменьшает количество вещей, которые могут произойти в приведенных ниже строках кода. Это, в свою очередь, делает код более предсказуемым и, следовательно, его легче читать и обдумывать.

Выражения

Второй и третий операнды тернарного оператора (resultIfTrue и resultIfFalse в приведенном выше примере) могут быть только выражениями. Это контрастирует с показанной выше версией if-else, в которой блок кода может содержать любой код после оператора if или else. Это делает тернарный оператор более предсказуемым, чем if-else. Читатель может быть уверен, что результатом будет либо resultIfTrue, либо resultIfFalse, и поэтому ему не нужно искать какие-либо другие части кода.

Это облегчает мысленный разбор кода за доли секунды. Это также гарантирует, что определение переменной и ее назначение не будут случайно «расходиться», когда позже между ними будет добавлен дополнительный код.

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

const response = {
    body: data,
    status: error ? error.status : 200
}

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

Недостатки

Более абстрактный

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

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

Мало места

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

Менее гибкий

Точно так же, как тернарный оператор, принимающий только выражения, может быть преимуществом, это делает тернарный оператор менее гибким. Для очень сложной условной логики обычно предпочтительнее использовать if-else.

Эффектный стиль

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

Каждый операнд в одной строке

Если вы хотите использовать тернарный оператор, но все тернарное выражение не помещается в одну строку, это наиболее распространенный стиль:

const result = someBooleanFunction(arg1, arg2, arg3)
    ? aPrettyLongResultFunction(arg4, arg5, arg6)
    : anEvenLongerResultFunction(arg7, arg8, arg9)

Это легко привыкнуть к чтению, потому что каждый операнд находится в одной строке, что делает сразу очевидным, что есть что.

Многострочные операнды

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

const result = someCondition
    ? (
        // multiline expression here
    )
    : (
        // another multiline expression here
    )

В JavaScript этот стиль может позволить вам добавлять к существующим объектам, оставаясь при этом полностью неизменяемым по условию:

const result = someCondition
    ? {
        ...baseObject,
        extraProperty1: 'a',
        extraProperty2: 'b',
    }
    : {
        ...baseObject,
        extraProperty3: 'c',
        extraProperty4: 'd',
    }

Обратите внимание, что здесь выражения не заключены в (), так как они уже заключены в {}, что позволяет читателю понять, где начинается и заканчивается каждый операнд.

Вложенные тройки

Вложенность троичных выражений вызывает споры среди разработчиков. Действительно, существует множество примеров крайне трудночитаемых вложенных тернарных выражений, но так быть не должно.

Существует трюк, позволяющий очень эффективно и наглядно вкладывать тернарные выражения! Вы захотите поместить троичное выражение только в качестве последнего операнда всеобъемлющего троичного выражения. Проще говоря, вы хотите, чтобы ? и : всегда чередовались друг с другом, поэтому у вас никогда не будет двух ? без : между ними. Затем сделайте отступ, чтобы получить следующий формат:

const result =
    question1 ?
        answer1
    : question2 ?
        answer2
    : question3 ?
        answer3
    :
        defaultAnswer;

Ключевой трюк для понимания этого вложенного троичного стиля состоит в том, чтобы смотреть на каждое условие как на вопрос (оно даже заканчивается знаком вопроса!). Затем на строке ниже отступом дается «ответ» на вопрос. Это может читаться очень естественно, потому что в мире без кодирования также используется этот формат с отступом.

Представьте себе пример из реальной жизни, где мы хотим определить адресную строку для конкретной страны, и это выглядит так:

Очень лаконично и полностью неизменно! При желании вы можете поставить новую строку перед каждой : дополнительной ясностью.

Заключение

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

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

Краткое примечание о Python

В Python тернарные операторы выглядят немного иначе. Вот пример:

name = expressionIfTrue if condition else expressionIfFalse

По сути, он работает так же, как и стиль ?:, хотя важно понимать, что порядок параметров другой. Состояние находится в середине, а не в начале.

Преимущество этого заключается в том, что оно больше похоже на английское предложение, и поэтому его легче понять интуитивно. Недостатком является то, что два выражения больше не являются смежными, а разделены условием. Этот синтаксис не позволяет им быть вложенными почти так же хорошо, как ?:.

Поэтому я бы рекомендовал проявлять большую осторожность при вложении тернарных операторов в Python. Вместо этого вы можете использовать синтаксис match case. Это, безусловно, более многословно, но будет гораздо более читаемым, и этот компромисс может стоить того. Другой вариант — выделить всю логику в отдельную функцию и использовать операторы if elif else, за каждым из которых сразу следует return.

Если вы знаете стиль, который делает вложенные тернарии читабельными в Python, сообщите нам об этом в комментариях!