Чистый или хороший код — это код, который может быть легко и приятно прочитан и понят программистами с первого взгляда. Кроме того, его легко изменить и не нужно переписывать.
Пишем чистый и читаемый код: руководство для начинающих разработчиков
Перевод статьи «The junior developer’s guide to writing super clean and readable code».
Уметь писать код — это одно, а уметь писать чистый код — совсем другое. Но что такое «чистый код»? Я создал это краткое руководство, чтобы помочь вам разобраться в этом вопросе и научиться искусству написания чистого кода.
Представьте, что вы читаете статью. Посмотрев вступление, вы сможете понять, о чем идет речь. Есть также заголовки, за которыми следуют несколько абзацев. Однако сами абзацы представляют собой логические блоки смысла, сгруппированные определенным образом, чтобы повествование текло и читалось хорошо.
Теперь представьте, что в этой статье нет заголовков. Есть разделение на абзацы, но они очень длинные и странно расположены. Вы не сможете быстро прочитать статью. Чтобы понять, о чем идет речь, нужно очень глубоко вникнуть в содержание. И это может быть очень раздражающим!
Ваш код должен читаться как хорошая статья. Вы можете думать о своих классах/файлах как о заголовках, о методах как об абзацах, а о строках как о предложениях. Вот некоторые характеристики чистого кода:
- Чистый код сфокусирован: каждая функция, класс и модуль должны делать какое-то одно действие, но делать его хорошо.
- Он должен быть элегантным. Чистый код прост для чтения. Когда вы читаете такой код, у вас должна непроизвольно появляться улыбка. У вас должна быть уверенность, что вы точно знаете, что именно делает этот код.
- Чистый код это заботливо написанный код. Кто-то потратил время, чтобы сделать его простым и упорядоченным. Этот человек уделил соответствующее внимание деталям. Он явно был неравнодушен к своему делу.
- У вас не должно возникать проблем с тестами: чистый код не может быть сломанным!
Теперь перейдем к главному вопросу: как молодой программист учится писать такой код? Я рекомендую следующие советы.
Используйте последовательное форматирование и отступы
В книгах, если бы межстрочный интервал и размер шрифта постоянно менялись и переставлялись по желанию, эти книги было бы очень трудно читать. То же самое относится и к вашему коду.
Чтобы сделать код понятным и читабельным, убедитесь, что отступы, межстрочные интервалы и форматирование согласованы (одинаковы в пределах файла/проекта). Ниже приведены примеры хорошего и плохого кода:
Хорошо
Функция getStudents(id)иначе >
- Лишь взглянув на код, вы видите, что там внутри функции есть блок if/else.
- Благодаря последовательности в отступах и расположении скобок легко видеть, где начинаются и кончаются блоки кода.
- Скобки располагаются последовательно. Обратите внимание, что открывающие скобки function и if расположены одинаково.
Плохо
function getStudents(id) if (id !== null) go_and_get_the_student();>else abort_mission();>>
Вау. Здесь много неправильных вещей.
- Беспорядочные отступы – невозможно сказать, где кончается функция или где начинается блок if/else (да, здесь он есть!).
- Скобки перемешаны и непоследовательны.
- Нелогичная разбивка кода на блоки с помощью межстрочного интервала.
Конечно, наш пример утрирован, но он хорошо иллюстрирует преимущества последовательного отступа и форматирования. Не знаю, как вам, а мне первый пример читается гораздо легче!
К счастью, существует множество плагинов для IDE, которые могут автоматически форматировать ваш код. Аллилуйя!
- VS Code: Prettier
- Atom: Atom Beautify
- Sublime Text: Prettify
Используйте понятные имена для переменных и методов
В начале я говорил о том, насколько важна читабельность кода. Кроме всего прочего, большую роль в этом играет именование. Давайте рассмотрим пример хорошего именования:
function changeStudentLabelText(studentId)функция getStudentName(studentId)
Эта часть кода хороша по многим причинам:
- Функции и аргументы имеют понятные, хорошие имена. Когда разработчик читает этот код, у него в голове сам собой выстраивается текст: «Если я вызову метод getStudentName() со studentId, я получу имя студента».
- Внутри метода getStudentName() переменные и вызовы метода также имеют понятные имена. Нам несложно понять, что метод вызывает API, принимает объект student и возвращает свойство name.
Для начинающих программистов выбрать хорошие имена для своего кода гораздо сложнее, чем кажется. Когда вы начинаете разрабатывать свое приложение, вы должны следовать этим соглашениям, чтобы убедиться, что ваш код читабелен:
- Выберите определенный стиль нейминга и придерживайтесь его. То есть, нужно использовать camelCase или under_scores, но не то и другое одновременно!
- В именах функций, методов и переменных должно отражаться то, что они делают, или то, чем они являются. Если ваш метод что-то принимает, вставьте в имя «get». Если ваша переменная хранит цвет машины, назовите ее, например, carColour.
Бонусный совет: Если вы не можете придумать название для своей функции или метода, скорее всего, он делает слишком много всего. Разбейте ее на более мелкие части! Например, если вы хотите, чтобы ваша функция называлась updateCarAndSave(), создайте два разных метода: updateCar() и saveCar().
Общие правила написания чистого кода
В начале повествования автор уделяет внимание основам создания правильного кода. Прочитав эту часть, я понял, что очень важно подумать о том, какие проблемы и ошибки могут возникнуть, прежде чем передавать код тестировщикам. Необходимо понять, какой пример не был рассмотрен. Полезно тщательно обдумать свою работу и только потом браться за нее. Потому что если вы начнете писать код, не продумав все до мелочей, вы можете столкнуться с ошибками, и вам придется возвращаться назад, другими словами, вам придется делать двойную работу. Я считаю этот совет очень важным. Если я применяю его сейчас, я экономлю много времени и сил.
Автор сравнивает написание кода с написанием книги. В обоих случаях вы сначала пишете черновик, в котором все шаги продуманы заранее, и только потом пишете окончательный вариант, который содержит лучший вариант. Если программист хочет быть хорошим программистом, он всегда должен заранее продумывать процесс.
Кроме того, не бойтесь что-то изменить в уже написанном коде. Разработчики часто работают с чужим кодом и не изменяют некоторые элементы, потому что боятся столкнуться с непредвиденными ошибками. По мнению автора, эта стратегия ошибочна, поскольку она неизбежно приведет к увеличению количества ошибок. Более того, одной из важнейших аксиом чистого кода является принцип единой ответственности. Согласно этому принципу, каждая единица должна выполнять только одно действие и нести только одну ответственность.
Автор также указывает, что код следует читать сверху вниз. Лучше организовать функции таким образом, чтобы при чтении одной из них, другая была сразу видна. Если разделы кодекса находятся в разных местах, читать их становится очень трудно.
Наконец, Р. Мартин указывает на важность удаления мертвого кода. Это код, который никогда не будет работать или который больше не используется. По мнению автора, эти коды бесполезны и должны быть немедленно удалены, чтобы все было структурировано. Помимо этого, я хотел бы остановиться на тех частях книги, которые кажутся мне наиболее важными.
Наименование
При написании кода очень важно определить правильное имя, поскольку имя обеспечивает читаемость кода. Важно использовать максимально понятные и подходящие имена. Когда технический специалист читает название метода или сущности, он должен сразу понять, за что она отвечает. Если программист следует этому принципу, то вряд ли функция будет содержать введения «And» или «With», так как функция будет выполнять только одно действие. Важно всегда использовать глаголы при описании функции. Желательно также иметь в имени ключевое слово, чтобы было понятно, о чем идет речь в функции.
Важно избегать одинаковых названий. Если в вашем коде есть функции с одинаковыми именами, они, вероятно, делают одно и то же. Если в технике есть именованные функции с одинаковыми именами, но у них разные задачи, следует переименовать их, чтобы было понятно, что они делают. Если они выполняют одно и то же действие, но, например, изменяют один и тот же параметр, вам нужно подумать, как сделать так, чтобы функция не повторялась. Другими словами: Если две функции имеют одинаковые имена, скорее всего, что-то нужно изменить.
Лучше всего не сокращать имена, а писать их полностью. Например, если используется слово «продукт», то название «pr» не очень понятно читателю. Важно, чтобы название полностью отражало ответственность учреждения.
Функции
По мнению Р. Мартина, операции должны быть как можно короче. Их длина не должна превышать двадцати строк, а сами строки не должны быть длиннее ста пятидесяти знаков. В противном случае лучше разделить их на части. Кроме того, важно, чтобы функция выполняла только одну функцию. Что касается входных параметров, то в идеале их не должно быть. Однако, если требуются входные параметры, их должно быть не более трех. Чем больше входных параметров, тем сложнее считать функцию.
Слишком большое количество комментариев в коде означает, что код написан плохо. Комментировать код следует только в том случае, если он не может быть понят другим программистом или автором кода, если он был написан давно. Кроме того, не нужно комментировать плохой код, его нужно просто переписать. Например, если специалист начинает работать над проектом другого разработчика и находит ошибки, они должны быть немедленно исправлены.
Комментарии к коду: хорошие, плохие и уродливые комментарии
Неполный или неправильно работающий код всегда должен быть прокомментирован одним из двух ключевых слов «TODO» или «FIXME». В этом случае программист сразу поймет, что код не закончен и нуждается в доработке. Программисты также часто комментируют функции или фрагменты кода только для того, чтобы вернуться к ним в какой-то момент, и это распространено в разных отраслях. Лучше не оставлять такие комментарии. Системы контроля версий хранят все состояния кода, поэтому нет необходимости переносить закомментированный код из одной ветки в другую.
Характеристики чистого года
- Он должен быть элегантным — чистый код должно быть приятно читать. Его чтение должно вызвать у вас улыбку, как это сделала бы хорошо сделанная музыкальная шкатулка или хорошо спроектированный автомобиль.
- Чистый код сфокусирован — каждая функция, каждый класс, каждый модуль демонстрируют целеустремленность, которая никогда не размывается и не загрязняется окружающими подробностями.
- Чистый код это забота. Кто-то нашел время, чтобы сделать это простым и упорядоченным. Люди уделили должное внимание деталям. Они позаботились.
- Он проходит все тесты.
- В нем нет дубликатов.
- Сведено к минимуму количество сущностей, таких как классы, методы, функции и т.п.
Одно из различий между умным программистом и профессиональным программистом заключается в том, что профессионал знает, что ясность является ключевым фактором. Профессионалы используют свои навыки во благо и пишут код, который могут понять другие — Роберт С. Мартин
Как писать чистый код?
Осмысленные имена
Выбор хороших имен требует времени, но экономит больше, чем стоит. Имя переменной, функции или класса должно отвечать на все важные вопросы. Он должен рассказать вам, почему объект существует, для чего он нужен и как он используется. Если название требует комментария, оно не должно выдавать его намерений.
— Имена классов
Классы и объекты должны иметь осмысленные имена или выражения, такие как Customer, WikiPage, Account и AddressParser. Избегайте в названии класса таких слов, как «менеджер», «процессор», «данные» или «информация». Имя класса не должно быть глаголом.
— Имена методов
Методы должны иметь глагольные имена или глагольные фразы, такие как postPayment, deletePage или save. Аксессоры, мутаторы и предикаты должны быть названы в соответствии с их значением и иметь префиксы get, set и соответствовать стандарту.
— Используйте доменные имена в случае проблемы
Если нет «определения» для того, что вы делаете, используйте название из проблемной области. По крайней мере, программист, работающий с вашим кодом, может спросить эксперта, что это значит.
Написание кода с использованием принципов S.O.L.I.D.
Термин SOLID был придуман Робертом К. Мартином (дядей Бобом) и описывает набор принципов для хорошего кода.
Принцип единой ответственности — SRP
Это означает, что у каждого занятия должна быть цель. У класса никогда не должно быть более одной причины для изменения. Если вы можете добавить в свой класс все, что хотите, это не значит, что вы должны это делать. Разделяйте большие классы на меньшие и избегайте «крестных» классов.
Рассмотрим пример. У нас есть RecyclerView.Adapter с бизнес-логикой внутри onBindViewHolder.
RecyclerView.Adapter не имеет единой ответственности, поскольку имеет бизнес-логику внутри onBindViewHolder. Хотя этот метод отвечает только за данные в реализации привязки представления.
Принцип открыт-закрыт — OCP
Программные объекты должны быть открыты для расширения, но закрыты для изменений. Это означает, что если вы пишете класс A, а ваши коллеги хотят изменить функцию в классе A, они могут легко сделать это, расширив класс A, а не внося изменения в класс A.
Простым примером является класс RecyclerView.Adapter. Вы можете просто расширить этот класс и создать свой собственный адаптер с настраиваемым поведением без изменения существующего класса RecyclerView.Adapter.
Принцип замен Лискова — LSP
Подчиненные классы никогда не должны нарушать определения типов родительского класса.
Это означает, что подкласс должен переопределять методы родительского класса, чтобы не вмешиваться в функциональность родительского класса. Например, вы создаете класс интерфейса со слушателем onClick(), применяете слушателя к MyActivity и передаете ему всплывающий маркер при вызове onClick().
Интерфейс ClickListenerclass MyActivity: AppCompatActivity(), ClickListener/.>>
Принцип разделения интерфейса — ISP
Принцип разделения интерфейсов (ISP) гласит, что ни один клиент не должен зависеть от методов, которые он не использует.
Чистый код: вывод
Зрелые разработчики знают, что идея о том, что все является объектом, — это миф. Иногда вам нужны простые структуры данных с выполняемыми на них процедурами. С этого момента вам нужно подумать о том, что необходимо внедрить, и о будущем, в котором чистый код можно будет легко обновлять.
Я знаю, что в прошлом вы создавали приложения с бессмысленными ярлыками, божественными классами и спагетти-кодом — поверьте мне, я делал то же самое. Вот почему я делюсь с вами своими знаниями о чистом коде дяди Боба. И для меня это тоже напоминание. Надеюсь, я помог вам понять принципы.
Как сделать код чистым? Рефакторинг!
Понятные имена, небольшие функции, отсутствие комментариев для объяснения кода…. все ясно. Но как это сделать? Как вы пишете код в соответствии с этими концепциями?
Наша задача сложна сама по себе. Написание рабочего кода само по себе может быть сложной задачей. И это без заботы о чистоте.
Поэтому рефакторинг может быть здесь ключевым моментом. Хороший подход — сначала написать код, не заботясь о чистоте, а затем очистить уже работающий код с помощью рефакторинга.
Рефакторинг
Рефакторинг кода — это процесс реструктуризации существующего кода без изменения его внешнего поведения. Это означает, что код должен работать одинаково до и после рефакторинга.
Примеры того, что не является реконфигурацией:
- Изменение алгоритма.
- Замена одного типа цикла другим.
- Повышение производительности фрагмента кода.
- Извлечение фрагмента кода в функцию.
- Переименование.
- Извлечение нескольких функций в новый класс.
- Создание константы для хранения жестко заданного значения в коде.
- …
Безопасный рефакторинг
Вы можете подумать: «Я не хочу ломать код, он прекрасно работает!». Вы можете подумать: «Я не хочу работать над этим, я не хочу, чтобы это работало! Достаточно сложно написать работающий код, вы не хотите его сломать.
Самая простая реструктуризация — это переименование. Например, у вас есть сущность с именем, которое вам не нравится, и вы хотите его изменить. Конечно, вы можете отредактировать его вручную, но это не так просто, потому что сущность может использоваться во многих местах.
- Тестирование. У вас должны быть хорошие автоматизированные тесты по многим причинам. И очевидно, что они помогут провести рефакторинг, ничего не сломав. После каждого рефакторинга вы можете проверить, все ли тесты по-прежнему зеленые. В этом посте я не буду писать о тестировании, возможно, в следующем. Если вы не знакомы с тестированием, вам стоит изучить этот вопрос. В сети есть много информации.
- Инструменты рефакторинга. В современных IDE есть инструменты, которые автоматически выполняют некоторые из наиболее распространённых рефакторингов. При их использовании снижается вероятность того, что мы что-то сломаем при внесении изменений в код. Я расскажу о некоторых из них далее в этой статье.
Когда следует проводить рефакторинг кода?
Например, я хочу переименовать класс Input в WordFrequency.
- Написание кода.
- Написание тестов.
- Рефакторинг.
Поскольку это класс, существует много возможных мест, где мне нужно изменить имя Input. Я должен найти их все вручную. Но у нас есть инструмент для изменения конфигурации:
Приложение. Инструменты рефакторинга
Этот инструмент переименовывает класс, а также все остальное, что нам нужно: различные варианты использования, имена файлов, тесты и даже имена переменных, которые могут быть связаны с нашим классом:Этот рефакторинг я использую чаще всего. Давайте рассмотрим пример:2. я использую инструмент рефакторинга Extract Method. Этот инструмент также имеет некоторые параметры:
Переименование (rename)
3. теперь код передается в метод, и вместо него выполняется вызов метода. Инструмент создает метод, перемещает туда код и изменяет все места, где тот же код присутствовал в одном вызове метода (ищет дублирующий код).
Существует множество инструментов реорганизации для популярных действий, выполняемых при реорганизации кода. Говоря о IntelliJ Idea, вы можете взглянуть на
свою документацию для изменения конфигурации.
Я рекомендую вам изучить все инструменты рефакторинга, доступные для вашей IDE, и поэкспериментировать с ними, чтобы увидеть, как они работают и насколько полезными вы их находите.
Извлечение метода (Extract method)
Чтобы всегда было понятно, что они хранят, декларируют или делают.
- У меня есть фрагмент кода, являющийся частью очень большой функции, которую я хочу отрефакторить. Я думаю, что часть этих строк делает какую-то законченную операцию и поэтому ее можно вынести в приватную функцию.
Избегайте очень коротких и непроизносимых имен.
Не переусердствуйте: слишком длинные имена также вредны. Никто не будет их читать, и они занимают слишком много места. Не используйте аббревиатуры, если они не общеизвестны.
Другие инструменты
Копирование и повторение кода во многих местах — это плохо. Вот почему:Помните, чем больше и чаще вы копируете код, тем больше времени вы теряете.Вместо того чтобы копировать, отредактируйте код так, чтобы он работал для старых и новых целей. Короче говоря, отредактированный код должен работать везде.
Правила создания чистого кода
Пишите понятные названия переменных, классов, функций
Как это сделать:
Примитивными типами являются int, string и bool. Очень часто, особенно при работе с булевыми значениями, лучше всего инкапсулировать примитивный тип. В противном случае сигнатура функции затрудняет понимание. Это означает, что легко совершить ошибку. Например, если функция возвращает значение bool, не всегда понятно, является ли false ошибкой или отрицательным результатом.
- Плохо : a1 или cnt. // Вам что, символов жалко?
- Нормально : count
- Хорошо : letters_count etc // Cразу ясно, что вы собрались посчитать.
Если функция имеет несколько аргументов одного типа, вы можете случайно поменять их местами при ее вызове, не получив ошибки компиляции.
Не копируйте код
Чтобы избежать вышеупомянутых ошибок, вы можете обернуть/поменять местами примитивные типы
- Скопированный код занимает много места — уходит много времени на чтение и отладку кода.
- Есть вероятность ошибиться при копировании логики и забыть изменить что-то специфическое.
- Постоянная угроза набагать из-за возможной неконсистентности скопированных кусков кода при последующем рефакторинге. Например, если вы изменили что-то в одном месте и забыли скопировать в другое.
- Дополнительное время при рефакторинге: вместо того чтобы обновить код однажды, нужно обновить его во всех местах, куда он был скопирован.
Идеального количества строк кода на метод или класс не существует, но всегда думайте о тех, кто будет читать ваш код. Хороший размер функции — около 20 строк на класс, чтобы другим не приходилось бесконечно прокручивать ее.
Прежде чем начать писать функцию, помните, что она не должна делать много вещей одновременно. Разделите ее на несколько частей, так же как вы делите большую проблему на несколько мелких подзадач.
Чем больше аргументов у функции, тем легче ее перепутать, пропустить что-то мимо ушей и ошибиться. Эта проблема обычно решается естественным образом, если следовать предыдущему совету и сделать функции небольшими и выполняющими одну задачу.
- Вынесите общую часть в отдельную функцию и вызовите из нескольких мест.
- Если не получается вынести сразу, попробуйте параметризовать или добавить условий, чтобы можно было вынести в отдельную функцию.
Инкапсулируйте примитивные типы
И если вам нужно десять аргументов для функции, состоящей из 15-20 строк, то так и сделайте:
— Обернуть определенные элементы в структуру,
— или получить значения внутри функции, передав ID в качестве аргумента, и выполнить поиск в базе данных внутри функции.
- Плохо : function getResultFromState(int state) : bool.
- Хорошо : function getResultFromState(State state) : Result / enum State, enum Result .
Минимизируйте уровни вложенности
Многие компании разрабатывают хорошее программное обеспечение для помощи в отладке. Например, компания Jetbrains разработала IntellIJ Idea для разработки на Java и Kotlin, а также линейки продуктов PyCharm и CLion для других языков программирования. Microsoft создала код Visual Studio, и так далее.
Чтобы обращать внимание на «предупреждения», можно установить глушилку.
Обзор кода — одна из самых полезных идей и один из самых быстрых способов научиться писать чистый код. Идея заключается в том, что вы показываете свой код другому разработчику и просите его об обратной связи.
Используйте небольшие классы и методы
Важно, чтобы разработчик, которого вы попросите провести рецензирование, хорошо понимал поставленную задачу и мог сам написать хороший, чистый код. Например, вы можете попросить рецензию у профессора университета или опытного коллеги по работе.
Не всем нравятся обзоры кода: Этот процесс требует как вашего времени, так и времени рецензентов. А если вы все еще учитесь писать чистый, хороший код, то для пересмотра может потребоваться несколько итераций и правок. Но помните, что обзоры кода полезны для вашего кода. Выбирайте рецензента с умом и будьте благодарны.
Это нормально — совершать ошибки. Гораздо хуже совершать одну и ту же ошибку снова и снова. Анализируйте решения и ошибки после каждого обзора кода.
Минимизируйте количество аргументов функции
Напишите краткий список правил или изменений, которые вы хотите внедрить. Например, вы хотите свести к минимуму вложенность. Запишите: «Минимизировать гнездование». И каждый раз, когда вы будете проверять свой код на наличие вложенных if и циклов, вы заметите это. Размышляйте после каждого обзора кода, чтобы не повторять ошибок.
Это значительно облегчит вам жизнь. Если вы знаете, как библиотечная функция