Книга Роберта Мартина Чистый код изменила мое представление о коде и его способ написания.

Почему чистый код?

Взгляните на изображение ниже, которое взято из книги.

Я люблю этот образ. Это ясно показывает разочарование разработчиков, читающих плохой код.

Чистый код приводит к коду, который другие разработчики могут быстро понять. Разработчики могут легко изменять код, не нарушая существующие функции.

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

В этом посте кратко изложены основные понятия из книги

Модульные тесты

Код не является чистым, если он не имеет модульных тестов.

Чтобы понять важность модульных тестов, рассмотрите эти вопросы

  • Как узнать, не нарушают ли внесенные вами изменения в код существующие функции? Собираетесь ли вы снова протестировать их вручную?

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

  • Рефакторинг — теперь вы знаете лучший способ кодирования, собираетесь ли вы изменить старый код, чтобы использовать его по-новому, или вы боитесь прикасаться к существующему коду?

Модульные тесты избавляют от страха касаться существующего кода. Избегание изменений в существующем коде не является решением. Сделать код легко изменяемым — это правильный путь.

Абстракция

Учтите, что вы читаете код, который взаимодействует с разными системами для получения/отправки данных, а затем в той же функции (которую вы читаете) выполняются некоторые низкоуровневые вычисления, такие как математическая операция или конкатенация, этот тип кода считается грубым .

Концепции высокого уровня должны быть отделены от концепций низкого уровня.

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

Разделение запросов команд

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

Функция, которая является Командой, не вернет никакого значения, это означает, что она будет иметь какой-то побочный эффект, то есть она изменит состояние системы. Например. setPrice(newPrice)

Функция, которая является запросом, что-то вернет, и у нее не будет никаких побочных эффектов. Запрос без параметра просто вернет значение. Запрос с параметрами будет просто работать со своими параметрами, чтобы вернуть результат. Ее также можно назвать чистой функцией. Например. getPrice(), sum(1,2). Чистые функции всегда будут возвращать одно и то же значение, если параметры не изменены. Для таких функций легко писать тесты.

Избегайте возврата null или кодов ошибок

Если из функции возвращается null, вызывающая функция должна немедленно обработать случай, если значение равно null. Если не обрабатывать такие случаи, может возникнуть знаменитая ошибка NullPointerException.

Как избежать null?

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

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

Такие языки, как Java, могут заставить обрабатывать такие ошибки в вызывающей функции.

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

function getResult(): Result | Err {}

Функция, возвращающая коды ошибок

Возвращаемые коды ошибок заставят вызывающую сторону немедленно обработать все различные типы кодов. Вместо этого может быть выброшена ошибка. Так что вызывающая функция может использовать try-catch для обработки ошибки в блоке catch.

Использование try-catch отделяет счастливый путь от пути ошибки и четко указывает, что код обрабатывает ошибки.

Уменьшение параметров функции

Чем больше параметров у функции, тем сложнее ее понять

Функции с параметрами 0 или 1 хороши. 2 еще не такие сложные, а вот 3 должны быть очень редко

Параметры можно уменьшить, создав классы/объекты. Если вы заметили, что один и тот же набор параметров передается нескольким функциям, это указывает на то, что в этом скрыта концепция. Эти функции могут быть сгруппированы в класс (с именем концепции), и конструктор этого класса может принимать эти общие параметры. Таким образом, этим функциям не нужно будет передавать параметры, поскольку они могут использовать их из частных свойств.

Избегайте комментариев

Закомментированный код бесполезен, если мы используем систему контроля версий, такую ​​как Git. С его помощью мы можем легко увидеть предыдущие версии файлов. Также такие комментарии теряют смысл через несколько месяцев/лет. Люди забудут, почему он был прокомментирован, или эти комментарии переместятся на другую строку после многих изменений в этом файле. Большинство читателей проигнорируют такой комментарий.

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

Значимые имена

  • Имена классов должны быть существительными, такими как Processor, Customer
  • Имена методов должны быть такими глаголами, как getPage. Такие префиксы, как get, set и is, можно использовать, чтобы указать, является ли метод получением, установкой значения или проверкой условия соответственно. например getName, setEmail, isHoliday

Избегайте использования else

Это не то, что я нашел в чистом коде, а в этом видео

Отсутствие else упрощает чтение кода

Учти это

if (condition) {
  doThis()
} else {
  doThat()
}

else можно избежать за счет досрочного возвращения

if (condition) {
  doThis()
  return
}
doThat()

Или в случае присваивания можно использовать значения по умолчанию вместо назначенного значения по умолчанию в блоке else

let a = 'default'
if (condition) {
  a = 'changed'
}

Для вложенного блока if-else можно создать отдельную функцию для вложенного блока if-else и использовать ранний возврат.