Программирование
Принцип KISS для Swift-разработчиков
Статья пытается показать, можно ли заменить трудный для понимания и подробный код более простым и лаконичным, сохраняя при этом то же поведение. Таким образом, она следует принципу KISS.
Что такое принцип KISS?
KISS — это аббревиатура от слова Keep it simple, stupid. Или формально: «Делайте это просто и понятно» (Keep It Simple and Straightforward). Это принцип, который гласит, что система должна быть спроектирована таким образом, чтобы впоследствии было легко понять внутреннее устройство. В результате внесение любых изменений потребует минимальных усилий. Считается, что принцип был изобретен авиационным инженером Келли Джонсон. Создавая реактивный самолет в качестве ведущего инженера, Келли направлял своих конструкторов так, чтобы система оставалась достаточно простой, чтобы любой, у кого есть начальная подготовка механика и основные инструменты, мог отремонтировать ее в боевой обстановке. Если система работает очень хорошо, но переконфигурировать/отремонтировать сложно, значит, конструкция не соответствует принципу KISS. Этот принцип актуален и полезен для любой области разработки. Например, для аэрокосмических инженеров в разработке авиационных систем, для производителей телевизоров в создании пультов дистанционного управления и, конечно же, для инженеров-программистов для разработки программного обеспечения.
Цель статьи
В первую очередь программисты пишут код, пишут модульные/UI тесты, выбирают архитектуры и делают многое другое. Поскольку программирование является самым фундаментальным в программной инженерии, эта статья посвящена только этому. Она пытается показать, можно ли заменить трудный для понимания и подробный код более простым и лаконичным, сохраняя при этом то же поведение. Таким образом, она следует принципу KISS. Принцип объясняется путем сравнения фрагментов кода в пяти различных сценариях. Хотя сниппеты написаны на Swift, большинство основных идей можно применить и в аналогичных языках.
Мотивация использовать принцип KISS
Принципы разработки программного обеспечения, например SOLID, DRY, YAGNI, KISS и т.д., сводятся к общей цели — написанию здорового кода, который можно легко читать, расширять и поддерживать. Принцип KISS настоятельно рекомендует писать простой код. В этом есть много плюсов. Например:
- Облегчение жизни других: написание кода — это работа, но написание хорошего кода — это ответственность. Если кто-то напишет простой код, другие программисты могут легко его прочитать и понять. Это облегчает жизнь каждому.
- Самолюбие: часто разработчикам приходится откатываться назад и заглядывать в свой код, чтобы исправить ошибки или добавить новые функции. Написание детализированного и простого кода — это польза для самого себя.
- Лучший код: простой код — это не обязательно хороший код, но это характеристика хорошего кода. Если можно добавить простоты, то уже сделан один шаг к лучшему коду. Согласно «Бритве Оккама», «простейшее объяснение какого-либо явления с большей вероятностью будет точным, чем более сложные объяснения». Имея это в виду, если есть несколько способов написать фрагмент кода, самый простой, вероятно, будет самым аккуратным. Может показаться расплывчатым связывать программирование с чем-то вроде бритвы Оккама, имеющей глубокие корни в литературе и философии, но некоторые исследования уже пытались сгладить этот пробел.
- Лучший кодер: хотя это спорно, некоторые думают, что Альберт Эйнштейн однажды сказал: «Если вы не можете объяснить это шестилетнему ребенку, вы сами этого не поймете». Сказал он это или нет, идея имеет смысл. Способность выразить что-либо прямо и просто отражает твёрдое понимание этого. Таким образом, чем выразительнее вы умеете программировать, тем более опытным вы являетесь. Такого мастерства можно достичь практикой, опытом и, что наиболее важно, желанием.
- Код с меньшим количеством ошибок: немного самоуверен, но иногда разработчики склонны добавлять ненужные абстракции без надобности. Может быть, чтобы сделать код более многоразовым и модульным? Для этого нужно добавить несколько дополнительных слоев. Цель этого — просто удовлетворить абстракцию. Но реальный код, имеющий бизнес-логику, остается глубоко внутри туманного фасада. Через несколько дней появляется новое требование, и теперь старая абстракция уже не соответствует запросам без внесения некоторых изменений. Эти изменения добавляют новый код, который заставляют абстракцию соответствовать новым требованиям, а также гарантируют, что существующие требования не нарушаются. Часто этот код является условными операторами, и если с ними не обращаться осторожно, они могут ошибочные пограничные случаи. Простой код не противоречит абстракции. Однако абстракция должна быть целенаправленной и адаптивной. Так что в будущем она не станет уязвимой для ошибок.
Слова ничего не стоят, покажи мне (простой) код
Теперь давайте рассмотрим некоторые подходы и посмотрим, может ли простое кодирование улучшить читаемость и расширяемость.
Сокращения
Один из способов сохранить кодовую базу менее загроможденной — использовать сокращения там, где это возможно. Одним из таких сокращений является тернарный оператор (?:). Это очень элегантно, но зачастую им пренебрегают. Давайте рассмотрим приведенный ниже код.
Для приведенного выше кода предположим, что от внутреннего сервера получен логический флаг ответа, который сохранен в переменной isSuccess. В зависимости от значения цвет текста метки обновляется, а затем он будет использоваться для отображения некоторого сообщения пользователям. Здесь использовался оператор if-else.
Это можно написать с использованием тернарного оператора, как показано ниже, и результат будет таким же.
Этот код маленький и легко читаемый. Вместо четырех это всего лишь одна строка. Хотя остается спорным вопрос, означает ли меньшее количество строк лучший код, в данном случае это проще и легче, чем выражение if-else. Хотя одно предостережение — не использовать вложенные тернарные операторы, так как это может привести к затруднению чтения кода.
Некоторые другие сокращения: объединение Nil, сокращенный синтаксис параметров, добавление, затем присвоение (+ =) и т.д.
Адекватные альтернативы
Принцип KISS говорит, что следует помнить одну вещь — не надо прилагать больших усилий, чтобы достичь незначительного результата. Например, при определении метода можно сначала подумать о вычисляемом свойстве. Если вычисленного свойства достаточно, то нет необходимости использовать метод. В приведенном ниже коде есть метод getAgeAfterFiveYears(), который возвращает возраст человека через пять лет.
Метод можно заменить вычисляемым свойством. Вычисляемые свойства — это особый вид свойств, которые не хранят значения. Вместо этого они вычисляют значение на основе других свойств и возвращают его.
Теперь и метод, и вычисляемое свойство возвращают одно и то же значение. Итак, у них такое же поведение. Но для вызывающей стороны вычисленное свойство — это просто еще одно свойство, а не метод.
Короче говоря, если метод очень простой, например, он не принимает никаких аргументов, не выполняет тяжелой работы и т.д., то, вероятно, это не метод, а хороший кандидат на вычисляемое свойство. Здесь достаточно вычисляемого свойства, и его использование не только следует принципу KISS, но также следует так называемому Swifty способу написания кода. Всегда разумно искать альтернативные способы, которых достаточно.
ПРИМЕЧАНИЕ. Может быть непонятно, почему у getAgeAfterFiveYears() и ageAfterFiveYears нет ключевых слов return. Это потому, что они используют так называемый неявный возврат (Implicit Return).
Синтаксический сахар
Языки программирования уже предлагают разработчикам некоторый синтаксический сахар, чтобы они могли избежать написания подробного кода. Вспомним структуру Person, определенную в предыдущих примерах. Предположим, существует массив с именем people, который содержит объекты класса Person. Теперь цель состоит в том, чтобы узнать индекс элемента из этого массива, у которого свойство name равно «Chloé Zhao». Ниже приведен один из способов сделать это.
Альтернативный способ добиться того же результата — использовать метод firstIndex (where :) типа коллекции Array, предлагаемый самим языком Swift.
Некоторые из синтаксических подсластителей, предлагаемых Swift: завершающие замыкания, обертки свойств, if-let и т.д.
ПРИМЕЧАНИЕ. Прежде чем двигаться дальше, в первом примере использовался цикл for, для которого временная сложность составляет O(n). То же верно и для второго примера. Под капотом firstIndex(where:) выполняет ту же (или аналогичную) операцию. Поэтому последний пример предпочтительнее только для простоты. Он не улучшит временную сложность.
Функции высшего порядка
Функции высшего порядка — это функции, которые принимают одну или несколько функций в качестве аргументов или/и возвращают функцию в качестве своего результата. Это вещи из вселенной функционального программирования (FP). Некоторые языки являются симбиотическими, поскольку в них сосуществуют элементы, заимствованные из других языков. К счастью, Swift — один из них, поскольку его типы коллекций поддерживают некоторые функции более высокого порядка. Например, Sorted, Map, FlatMap, Reduce и т.д. В некоторых случаях функции высшего порядка могут добавить простоту и лаконичность коду. Предположим, что необходимо заполнить массив имен из массива людей, упомянутого в предыдущих примерах. Наиболее вероятный подход, который можно было бы использовать инстинктивно, был бы следующим:
То же самое можно сделать в стиле функционального программирования:
Это может выглядеть как синтаксический сахар. Но это совсем другое дело. Функции высшего порядка гораздо более гибкие по своей природе. Например, Map применяет общую операцию к каждому элементу коллекции. Операция обеспечивается closure в качестве аргумента.
На сайте raywenderlich.com есть хорошая статья Уоррена Бертона с примерами FP и функций высшего порядка.
Согласованность
В некоторых случаях полезно пожертвовать некоторой простотой, чтобы получить более расширяемый код. Это цена, уплачиваемая за достижение положительной согласованности всей кодовой базы. Давайте начнем с примеров.
Приведенный выше код, основанный на константе difficulty, печатает некоторую информацию о башнях. Представьте, что башни взяты из видеоигры Mortal Kombat 3. Очевидно, что if-else не выглядит лаконичным, и сопоставление строк небезопасно.
Это также не является перспективным, потому что, если появятся новые значения сложности, компилятор не сможет сказать, что нужно добавить новые блоки else-if. Программист должен будет добавлять их самостоятельно.
К счастью, с помощью Enum (с raw значениями), Typealias, Tuples и вычисляемого свойства все упомянутые проблемы могут быть решены.
Теперь всякий раз, когда появляется новый тип сложности, например, Легендарный, в перечисление Difficulty нужно просто добавлять только новое значение. Компилятор выдаст ошибку, и пока этот случай не будет учтен в операторе switch, код не будет выполняться. Так что программист не сможет по ошибке оставить дело без присмотра.
Вместо typealias можно было бы использовать структуру, но для этого нет веской причины. Потому что, скорее всего, структура не будет использоваться в других местах, поскольку она тривиальна по своей природе.
ПРИМЕЧАНИЕ: По-видимому, второй подход более подробен, чем первый. Более того, для новичков в Swift код может показаться отталкивающим. Но, несмотря на это, второй подход легко расширяем и безопаснее. Может быть, синтаксис и сложнее, но характеристики и удобство использования проще. Хотя это звучит как оксюморон, иногда простоты можно достичь за счет некоторого количества контролируемых усложнений, баланса между чрезмерной и недостаточной инженерией.
Принцип KISS: заключение
Спасибо всем, кто нашел время, чтобы прочитать. Надеюсь, это было информативно. Несомненно, написать хороший код непросто, и я считаю, что это долгий путь в карьере, и одной статьи или книги недостаточно. Я нахожусь на этом пути, и я попытался записать некоторые полезные советы, которые я узнал. Я также хотел бы честно признаться. В некоторых местах я звучал немного категорично. Потому что сложно давать ссылки на собственный опыт, а не цитировать какой-либо учебный материал. По большому счету, старайтесь следовать KISS, писать простой для чтения код, не переусердствовать. И мы, Swift-программисты, переопределим принцип KISS — «Будь простым и быстрым» (Keep it simple and Swifty) :)