Ваша комната может быть грязной, а код — нет, я не самый организованный человек в мире, и моя комната — не лучшее произведение искусства, и я слышу всю вселенную в голосе Моргана Фримена, говорящего человеку, что « Все в порядке», если у вас нет девушки или супруга, тогда это совсем другая история. Суть в том, что ваша комната — это ВАША комната, но когда вы работаете в команде из более чем 20 человек, код, который вы фиксируете, НЕ является вашим кодом, даже если вы единственный, кто работает над этим проектом, и у вас есть весь код. беспорядочный, грязный и неорганизованный - это очень плохо, и это будет стоить вам много времени на отладку и расширение.
Написание чистого кода очень важно, и там может быть много информации, но вот моя универсальная статья о чистом коде, она может быть длинной, но очень важной, и я собираюсь рассказать о многом. как писать чистый код, а также вы можете обратиться к этому пункту при написании своего кода и посмотреть, как вы можете сделать его лучше, а также время от времени читать его, чтобы вы знали его наизусть! Давайте начнем.
Прежде чем мы погрузимся в чистый код, давайте сначала начнем с этой замечательной цитаты Мартина Фаулера и поразмыслим над ней:
Любой дурак может написать код, понятный компьютеру. Хорошие программисты пишут код, понятный людям.
Хорошо иметь работающий код, без сомнения, можно написать грязный код, компилятор его компилирует, машина его понимает и все круто, однако, что, если вы хотите отладить этот код через несколько месяцев, что, если кто-то еще ваша команда хочет расширить функцию поверх грязного кода, который вы написали, это кошмар, и у вас, у других разработчиков, будет много моментов WTF. Так что лучше избавить себя и других от проблем и сделать свой код чистым, читаемым и понятным для всех.
Пишите код для людей.
Я рекомендую, прежде чем погрузиться в TDD, DDD, шаблоны проектирования, принципы SOLID, рефакторинг, лучше начать с чистого кода, его основы и матери всего!
Хорошо, давайте поговорим немного больше о философии и принципах Чистого Кода. Есть три принципа Чистого Кода:
- Правильный инструмент для работы
- Высокое отношение сигнал/шум
- Самодокументирование
Прежде чем пачкать руки и писать примеры чистого кода, важно понять принципы.
- Правильный инструмент для работы: если вы хотите воспроизводить музыку с высокой громкостью, то, черт возьми, вы можете использовать свой существующий ноутбук, и это может дать вам достаточно большую громкость, но лучше этого заключается в использовании динамиков со специальными усилителями, которые могут обеспечить высокое качество громкости, использование правильного инструмента для работы очень важно. В контексте чистого кода использование правильного инструмента для работы означает, что вы должны следить за границами и не смешивать разные инструменты вместе, вы хотите стилизовать свой веб-сайт, конечно, вы можете добавить встроенные стили в свой html-код, но это лучше Если вы разделяете свои стили в другом файле .css, я надеюсь, вы уловили идею, и вот несколько примеров и ограничений, на которые следует обратить внимание:
Ключевым моментом является то, что старайтесь оставаться нативным, когда это возможно, и избегайте использования одного языка для написания другого языка, старайтесь не смешивать технологии, и таким образом вы получите более пригодный для повторного использования, проверенный синтаксис, цветной код.
2: Максимальное отношение сигнал-шум: эта концепция действительно важна, как мы договорились ранее, ваш код должен быть написан для людей, сигналы — это полезная информация, которую вы хотите, чтобы вы или разработчики получили из кода, это намерение, шум – это то, что затрудняет понимание этого намерения таким образом, что ум не может легко понять, например, ненужные комментарии, неправильно названные переменные, огромные классы, повторение, без пробелов.
Подводя итог, ваши Sсигналы должны соответствовать правилу TED:
- Кратко: ваш код должен быть кратким и содержать только несколько осмысленных синтаксисов.
- Выразительность: помните, что ваш код должен быть самодокументируемым, поэтому вместо написания ненужных комментариев вы можете использовать функции и имена переменных, чтобы сделать код более выразительным и понятным.
- D сделайте одно.
Шум может быть:
- Ненужные комментарии
- Плохо названные структуры
- Высокая цикломатическая сложность (подробнее об этом позже)
- Огромные классы
- Длинные методы
- Нет пробелов
- Чрезмерные углубления .. и т.д.
Важность этого заключается в том, что наш мозг действует как компилятор, а беспорядок из-за высокого коэффициента шума накапливается и затрудняет понимание кода.
Принцип DRY. Наш разговор о соотношении сигнал/шум был бы неполным без упоминания знаменитого принципа DRY (Не повторяйтесь). и вам нужно следить за тем, когда вы копируете и вставляете код, потому что это в основном приводит к:
- Уменьшение отношения сигнал/шум
- Увеличение количества строк кода, больше строк кода означает, что у вас есть больше, чем необходимо для передачи вашего намерения.
- Создавая проблемы с обслуживанием, если вам нужно изменить часть кода, вам нужно убедиться, что вы изменили все остальные места, где этот код дублируется. Это беспорядок!
3: И наконец, третий принцип — самодокументирующийся код: если вы пишете выразительный код с четкими именами функций и понятными именами переменных, ваш код будет самодокументируемым. Важно не только организовать код, но и для организации ваших пакетов/пространств имен, классов и методов. Когда вы читаете книгу, она организована в главы, в этих главах у вас могут быть заголовки, чтобы объяснить, о чем следующий текст, и, наконец, у вас есть Абзацы, вы можете реализовать ту же концепцию в своем коде следующим образом:
Итак, Ключевые моменты:
- постарайтесь, чтобы ваш код самодокументировался и имел четкое намерение
- постарайтесь использовать лиры абстракции в том смысле, что это упрощает навигацию по вашему коду и классам, как в книге
- старайтесь отдавать предпочтение коду, а не комментариям
- сделайте отступ и отформатируйте код для более удобного чтения
Я надеюсь, что предыдущая Теория и Философия Принципов Чистого Кода подготовили вас к Соку, к Мясу, к фактическим примерам Чистого Кода, которые мы сейчас обсудим с фрагментами кода.
Далее я подробно расскажу о различных областях чистого кода, мы начнем с наименования ваших классов, ваших методов и ваших переменных. Затем мы коснемся того, как писать чистые условия, после этого мы поговорим о написании чистых функций , классов и, наконец, мы коснемся написания Комментарии. Давайте начнем:
Примечание: код, который я пишу, является АБСТРАКТНЫМ кодом и не обязательно принадлежит конкретному языку программирования, я обнаружил, что так легче передать цель статьи.
1. Именование. Именование имеет значение, четкие имена могут легко передать намерение и сделать ваш код более понятным:
Грязно:
List<decimal> p = new ArrayList<>() {5 , 10 , 20 , 30 } ;
Очистить:
List<decimal> prices = new ArrayList<>() {5 , 10 , 20 , 30 } ;
1.1 Именование классов. Они должны быть существительными и выполнять одно действие:
При написании классов назовите их в соответствии со следующими рекомендациями:
- Классы должны быть существительными, Чистые: Пользователь , Учетная запись , Продукт , ParkinLot
- Избегайте универсальных классов, которые могут стать магнитом, куда ленивые разработчики могут прийти и написать о них, поэтому будьте конкретны. Грязные: Утилиты , Общие
- Единая ответственность, ваш класс должен иметь единую ответственность, я не ожидаю, что ваш пользовательский класс когда-либо будет обрабатывать Payments, Dirty: класс MyFunctions
- Избегайте общих суффиксов , грязный*менеджер , *процессор
1.2 Методы именования должны ясно выражать намерение:
Название метода должно сказать все, и в основном это глагол, он должен четко выражать ваше намерение.
Грязно:
get (); process(); start(); destroy();
Очистить:
getRegisteredUsers(); isValidUser(); exportHistoryReportToExcel(); sendEmail();
1.3 Предупреждение, если вы видите «И», «Если», «Или»?
Вот некоторые признаки того, что ваши классы или функции выполняют несколько функций: Если вы когда-либо видели функцию или имя класса, которое имеет: И , Если , Или , это признак того, что они делают больше. чем что-то одно, и важно сделать шаг назад и разделить логику на разные функции или классы:
Грязно
signupAndSendVerificationEmail ();
Очистить:
signup(); sendVerificationEmail();
1.4 Опасайтесь побочных эффектов?
Побочные эффекты могут проявляться в форме, когда у вас есть функция, которая делает больше, чем заявлено:
- checkPassword() не должен выходить из системы пользователя
- signup() не должен отправлять электронные письма
1,5 Avd Abbr ?
Вы знаете, о чем мы говорим? умничка ты угадала! Избегайте сокращений! это уже не 70-е годы, и, имея мощные IDE, вы можете иметь автозаполнение из коробки, вам нужно избегать сокращений, потому что это создает двусмысленность и делает ваше намерение неясным и подвергает неправильному толкованию:
Грязно:
regUsr(); regisUsr(); calP();
Очистить:
registerUser(); calculateTotalPrice ();
1.6 Логические имена имен должны звучать как вопросы "верно/неверно"
Грязно: открыть , статус , войти
Очистить: isOpen, isActive, войти в систему
Грязные примеры не имеют характера истинно ложных вопросов, open может иметь намерение открыть что-то, и это больше похоже на функцию (глагол), однако, если вы используете isOpen, ваше намерение здесь ясно, что вы отвечаете на верный/ложный вопрос
Грязно:
if (login) { // do something }
Очистить:
if (loggedIn ) { //do something }
1.7 Будьте симметричны при именовании переменных, которые имеют дело с переключаемыми состояниями:
Грязно:
// on / disable // slow / max
Очистить:
// on / off // min / max
И это была большая часть того, что вам нужно знать об именовании, давайте теперь поговорим об условных выражениях, и по какой-то причине это моя любимая часть.
2: Условия:
Условные операторы — это развилка на дороге, и при написании условного кода важно понимать первоначальный замысел программиста, это может показаться интуитивно понятным, но при написании ваших условных операторов многое может пойти не так:
Понимание замысла исходного программиста — самая сложная проблема. (Фьелстад и Хамлен, 1979 г.)
Вот несколько рекомендаций, которым вы должны следовать при написании условных выражений:
2.1 Сравнение логических значений неявно и неявно:
Лучше сравнивать ваши логические значения неявно, будьте СУХИМИ, давайте возьмем эти два примера:
Грязное явное сравнение:
if (loggedIn == true ) { // do something }
Чистое неявное сравнение:
if (loggedIn) { // do something }
Неявное сравнение — это меньше кода, оно более интуитивно понятно в том смысле, что когда вы его читаете или когда его читают другие разработчики, оно звучит примерно так:
«Если вы вошли в систему, то сделайте что-нибудь»
В то время как явное сравнение будет звучать примерно так:
Если loggedIn верно, то сделайте что-нибудь
Явное сравнение не кажется интуитивным, и всегда полезно посмотреть, как ваш код будет звучать при чтении.
2.2 Неявное назначение логических значений:
Грязно:
bool goingBackToMyCountry ; if ( cashInWallet > 5000 ) { goingBackToMyCountry = true; } else { goingBackToMyCountry = false; }
Очистить:
bool goingBackToMyCountry = cashInWallet > 5000 ;
Теперь это читается как речь «Я вернусь в свою страну, если наличных в моем кошельке будет больше 5000» и имеет меньше строк, также у вас нет отдельной строки для инициализации.
2.3. Не выступайте против негатива: это создает путаницу!
Грязно:
if (!isNotLoggedIn )
Очистить:
if (loggedIn )
2.4 Используйте Ternary, это элегантно:
int entranceFee ; if (isLady ) { entranceFee = 0 ; } else { entranceFree = 50; // true story }
Очистить:
int entranceFee = isLady ? 0 : 50 ;
Не повторяйтесь.
2.5. Используйте перечисления и избегайте строковой типизации:
Грязно:
if ( status == "success" )
Очистить:
if (status == Status.Success )
Мы заметили следующие преимущества при использовании Enums:
- его строго типизировано и проверено
- вы можете перейти к перечислению статусов и просмотреть все доступные и возможные статусы
- его легко найти
2.6. Избегайте магических чисел!
Как мы упоминали ранее, очень важно понять исходное намерение программиста, когда у вас есть магические числа, которые понимаете только вы, другим разработчикам будет трудно узнать ваше намерение, возьмите это в качестве примера:
Грязно:
if (age > 21 ) { // do something }
21 – это магическое число, и вы могли его угадать, но мы не хотим, чтобы вы угадывали его, мы хотим, чтобы вы были уверены, что именно так и было задумано, здесь является лучшей реализацией:
Очистить:
int DRINKING_AGE = 21 ; if (age > DRINKING_AGE ) { // do something }
2.7 Избегайте сложных условий с помощью промежуточных переменных или их инкапсуляции с помощью функции:
Я был виновен в том, что когда у вас есть сложные условные операторы, которые проверяют разные условия, вы можете получить код без четкого намерения. давайте обсудим несколько примеров:
2.7.1 Использование промежуточных переменных:
Грязно:
if ( employee.age > 55 && employee.yearsEmplyed > 10 && employee.isRetired ) { // do something }
Как вы можете видеть, что намерение здесь не ясно! и я не думаю, что вы сможете догадаться, более элегантное решение — использовать промежуточные переменные, которые передают намерение:
Очистить:
bool eligibleForPension = employee.age > minRetirementAge && employee.yearEmployed > minPensionEmploymentYears && emplyee.isRetired ;
Здесь мы ввели промежуточную переменную eligibleForPension, которая четко передает намерение, а также избавились от магических чисел 55 и 10.
2.7.2. Инкапсуляция логики с помощью функции:
Самое замечательное в функциях то, что они могут объяснить сами себя, и они делают понятным намерение, давайте возьмем этот пример:
Грязно:
// check for valid file extentions if (fileExtention == "mp4" || fileExtention == "mp3" || fileExtention == "avi" ) { // do something }
Пожалуйста, предпочитайте выразительный код комментариям, вы можете извлечь эту логику в функцию:
Очистить:
private bool isValidFileExtention (string fileExtention ) { return (fileExtention == "mp4" || fileExtention == "mp3" || fileExtention == "avi" ) ;
Теперь это чище, чище было бы поместить все допустимые расширения файлов в массив или любую структуру данных и проверить, существует ли расширение файла, я оставлю это вам.
2.8. Будьте декларативными, если это возможно, используя правильный инструмент для работы:
Возьмем этот пример:
Грязно:
for (User user : users) { if (user.getBalance() > 50 && user.getStatus() == Status.ACTIVE) matchingUsers.add(user); }
Очистить:
matchingUsers = users.stream() .filter(user -> user.getBalance() > 50 && user.getStatus() == Status.ACTIVE ) .collect(Collectors.toCollection(ArrayList::new));
Когда вы пишете цикл for, вы действительно заботитесь о том, как выполнять фильтрацию с помощью оператора if, в то время как во втором случае вы заботитесь о том, что, ясно, что вы фильтруете пользователей на основе определенных критериев, и это использует правильный инструмент для работы.
Таким образом, эмпирическое правило будет более декларативным, и когда вы имеете дело с такими структурами данных, лучше использовать лямбда-выражения или любой другой инструмент вместо цикла for. Я упоминал об этом ранее в другой статье, что в настоящее время в современном программировании вы редко собираетесь использовать необработанный цикл for.
2.9 При работе с большим количеством операторов if код может не подойти:
Давайте возьмем этот пример, который является фрагментом метода расчета налоговой ставки:
Грязно:
if (salary < 3000 ) return 0; else if (salary < 6000 ) return 3; else if (salary < 10000 ) return 7; else if (salary < 15000 ) return 15;
Более чистый способ написать это не в коде, это может быть таблица базы данных с налоговыми ставками, и вы можете просто
Очистить:
return Repository.getTaxRate(salary);
Это называется табличными методами и имеет следующие преимущества:
- это отлично подходит для динамической логики, так как ставка налога может измениться
- это позволяет избежать жесткого кодирования
- это заставляет вас писать меньше кода
- его легко изменить без необходимости перекомпилировать/повторно развертывать ваш код
3: Функции: в этом разделе я расскажу о функциях, функции такие же, как абзацы в книге, и важно, чтобы у этих функций были четкие имена, сначала давайте начнем с того, когда использовать функции.
Есть четыре основные причины для создания функции:
- Избегайте дублирования, как программист, старайтесь соблюдать принцип DRY, код, который вы пишете, является обязательством, и, как гласит великое изречение:
Меньше - больше
Написание функций также поможет вам поддерживать свой код, если у вас есть дублирование кода и вы его меняете, вам нужно убедиться, что вы изменили все остальные места, где этот фрагмент кода дублируется.
2. Отступ: разработчики могут подумать, что предотвращение дублирования — единственная причина для написания функций, но это не так, даже если ваша функция вызывается один раз. Отступ — это признак сложности, и, как вы помните, понимание намерения — более сложная часть, извлечение логики с отступом в функции — еще одна причина для написания функций.
3. Неясное намерение: вы видите, как я много говорю о намерении, потому что это действительно важно в «Чистом коде». Если намерение неясно, вы можете извлечь логику с хорошо описательными именами функций, чтобы передать намерение.
4. Наличие более одной задачи: для соблюдения принципа единой ответственности важно, чтобы ваши функции выполняли только одну задачу, наличие функции, которая выполняет более одной задачи, является еще одной причиной для создания другой функции.
Теперь, когда мы знаем, когда писать функцию, давайте копнем глубже и посмотрим, как их написать на грязных и чистых примерах:
1- Избегайте дублирования:
3.1.1 ищите шаблоны, которые могут быть неочевидными:
Дублирование может быть неочевидным все время, вы можете использовать одно и то же регулярное выражение несколько раз, в этом случае вам нужно извлечь его, чтобы избежать дублирования.
2- Чрезмерный отступ:
Когда наша логика усложняется, часто можно увидеть код в форме стрелки, и это означает, что читатель кода должен держать все эти уровни в голове при чтении кода, это снижает читабельность и может быть источником ошибок и меньшей пригодности для тестирования. , если вы видите много отступов в своем коде, то это плохой знак, и его необходимо реорганизовать или написать чистым способом.
Для решения проблемы с кодом формы стрелки есть три возможных решения:
- Методы извлечения
- Вернуться раньше
- Сбой быстро
3.2.1 Избегайте чрезмерных отступов с помощью методов извлечения:
Картинка стоит тысячи слов:
Вы видите, как извлекается сложная функция и как она стала более читабельной, давайте возьмем другой пример и попробуем обобщить его:
Грязно:
try { // so // many // piece // of //logic // save // planet } catch (ArgumentOutOfRangeException e ) { // do something with the error }
Более чистым способом было бы извлечь логику блока Try в отдельную функцию.
Очистить:
try { saveThePlanetFromGlobalWarming(); } catch (SpaceSataliteNotFoundException e) // do somethng } private void saveThePlanetFromGlobalWarming () { // so // many // piece // of //logic // save // planet }
Итак, чтобы попытаться обобщить это правило, всякий раз, когда у вас есть блоки кода, такие как цикл while, оператор if, блок кода try, лучше попытаться извлечь логику в свои собственные функции, если это сделает ее более читабельной.
3.2.2 Избегайте чрезмерных отступов за счет быстрой ошибки:
Когда ваш код может дать сбой, лучше сделать это быстро, потому что если он дает сбой, то нет смысла продолжать до конца, давайте возьмем этот пример:
Вот грязный пример медленного сбоя:
public void registerUser (String username, String password) { if (!String.isNotNullOrWhiteSpace (username)) { if (!String.isNotNullOrWhiteSpace (password) { // register the user } else { throw new ArgumentException("username is required "); } else { throw new ArgumentException("password is required "); } }
Потерпите неудачу быстро! Чисто:
if (String.isNotNullOrWhiteSpace (username)) throw new ArgumentException("username is required "); if (String.isNotNullOrWhiteSpace (password)) throw new ArgumentException("passwordis required "); // register user here
Вы видите здесь, что мы инвертировали логику, когда логика терпит неудачу, в этом случае, когда имя пользователя или пароль пусты или содержат пробелы, и тогда мы быстро потерпели неудачу, потому что нет смысла продолжать сбойный код, и теперь это делает ваш код становится более читаемым и уменьшает проблему чрезмерных отступов.
3.2.3 Избегайте чрезмерных отступов, возвращаясь раньше:
Еще один почти аналогичный подход к быстрому отказу — вернуться раньше, постарайтесь использовать операторы возврата как можно раньше, и это сделает ваш код лучше и с меньшим отступом. давайте возьмем пример:
Грязно:
private bool isUserNameValid (String username) { int MIN_USERNAME_LENGTH = 6 ; if (username.length >= MIN_USERNAME_LENGTH ) { MAX_USERNAME_LENGTH = 25 ; if (username.length <= MAX_USERNAME_LENGTH ) { bool isAlphaNumeric = username.isAlphaNumeric (); if (isAlphaNumeric ) { if (!containsCurseWords(username)) { isValid = isUniqueUsername(username); } } } } return isValid; }
Чистая версия должна возвращаться раньше:
private bool isUserNameValid (String username) { int MIN_USERNAME_LENGTH = 6 ; if (username.length < MIN_USERNAME_LENGTH ) return false; MAX_USERNAME_LENGTH = 25 ; if (username.length > MAX_USERNAME_LENGTH ) return false ; bool isAlphaNumeric = username.isAlphaNumeric (); if (!isAlphaNumeric ) return false; if (containsCurseWords(username)) return false; return isUniqueUsername(username); }
Вы видите, как это уменьшило количество отступов и сделало код более простым за счет раннего возврата.
3. Выразите намерение:
Третья причина написания функции заключается в том, чтобы передать намерение, как упоминалось ранее, функции могут иметь четкое имя, которое передает намерение, давайте возьмем знакомый пример:
Грязно:
// check for valid file extentions if (fileExtention == "mp4" || fileExtention == "mp3" || fileExtention == "avi" ) { // do something }
Очистить:
private bool isValidFileExtention (string fileExtention ) { return (fileExtention == "mp4" || fileExtention == "mp3" || fileExtention == "avi" ) ; }
Еще более чистый способ — возвращаться раньше :
private bool isValidFileExtention (string fileExtention ) { if (fileExtention == "mp4" ) return true; if (fileExtention == "mp3" ) return true; if (fileExtention == "avi" ) return true; return false; }
4. Сделайте одно:
Это четвертая причина написать функцию, можете ли вы читать книгу без абзацев, наличие функции, которая делает одну вещь, может помочь читателю, даже если функция не используется повторно, если у вас много логики, попробуйте извлечь ее в функция со значимым именем. старайтесь избегать функций, которые выполняют более одной функции (побочный эффект).
Наше обсуждение функций было бы неполным, если бы мы не говорили о том, когда инициализировать переменные. Следует ли объявлять переменную в начале функции, а затем использовать ее позже? Вы должны инициализировать его как раз вовремя? давайте посмотрим на примерах:
3.5 Общие правила:
3.5.1 Инициализация переменных точно в срок:
Вы помните, как при чтении кода наш мозг является компилятором? когда вы объявляете переменную в одной строке и используете ее через 15 строк, вы не помогаете делу, всегда лучше инициализировать переменные как раз вовремя, сравните следующие два сценария:
Грязно:
bool a = false; int b = 9; String s = "Sabir" // save // the // earth // from // hunger a = isItTrue(); if (a) { if (s.length > b ) { // do // something } }
Очистить:
// save // the // earth // from // hunger bool a = isItTrue(); if (a) { String s = "Sabir"; int b = 9; if (s.length > b ) { // do // something } }
Ну, вы можете захотеть, чтобы ваши переменные имели более широкую область видимости, вы, черт возьми, можете это сделать, но обязательно инициализируйте вовремя, чтобы упростить чтение кода, но объявить переменную в какой-то строке, а затем инициализировать ее бог знает когда линия, не очень хорошая идея.
3.5.2 Стремитесь к 0–2 параметрам в функциях:
Функции с большим количеством параметров трудно тестировать, их трудно понять, и, скорее всего, они выполняют более одной функции, стремитесь, чтобы ваши функции имели только 0–2 параметра.
Грязно:
public void saveUser (String username, String password, int age , bool sendEmail)
Очистить:
public void saveUser (User user)
3.5.3. Следите за аргументами флага, это признак того, что ваша функция выполняет несколько действий:
Грязно:
public void saveUser (User user , bool sendEmail) { // save user if (sendEmail) { // email user } }
Очистить:
public void saveUser (User user) { // save user } public void emailUser (User user ) { // email user }
Таким образом, у вас нет побочных эффектов, когда сохранение пользователя фактически не отправляет электронное письмо пользователю, и если другие разработчики хотят просто сохранить пользователя без отправки электронного письма, они могут просто использовать функцию saveUser().
3.5.4. Избегайте очень длинных и коротких функций:
Длинные функции трудно переварить, это снижает читабельность и может быть признаком того, что функция делает слишком много:
Редко бывает более 20 строк Практически никогда не превышает 100 строк Не более 3 параметров Роберт С. Мартин, «Чистый код»
4: Классы:
Вторая последняя часть головоломки в чистом коде — иметь чистые классы. Если вы работаете над объектно-ориентированным языком, классы могут оказать огромное влияние на удобство сопровождения и ясность намерений. В этом разделе мы расскажем, когда писать классы и как их проектировать, обсуждая большое правило структуры.
Когда создавать курс:
Точно так же, как у нас есть причины для создания функций, есть причины для создания классов следующим образом:
- Новая концепция: вы пишете классы, когда у вас появляются новые концепции. Это помогает абстрагироваться от реального мира и сделать ваш код более доступным. путем создания хорошо спроектированных переменных-членов и методов. т.е. Класс планировщика встреч. Это может обеспечить высокий уровень намерения и где у вас может быть хорошо названный метод.
- Низкая связность: низкая связность означает, что методы в ваших классах не являются тесно связанными, следуя принципу единой ответственности, вы должны стремиться к высокой связности в своих классах. Если методы ваших классов имеют низкую связность, то это знак, что вам нужно в разные классы.
- Поощрение повторного использования: когда вы организуете свой код в классы, это способствует повторному использованию, даже если ваша логика находится в больших классах, лучше организовать ее в меньшую логику с меньшими классами, чтобы ее можно было использовать в другой программе. .
- Уменьшение сложности: когда вы пишете класс, вы решаете проблему, и когда приходит читатель, он/она может повторно использовать этот класс и верить, что написанный вами класс решит любую сложность, которую вы намеревались решить.
- Уточнение параметров: когда вы передаете много параметров в функцию (например, потерянные параметры пользователя, например: имя пользователя, пароль, возраст), создание классов для таких структур данных может уточнить параметры. и сделать его более легким для понимания, например, создать класс User в этом случае.
Теперь давайте поговорим о некоторых рекомендациях:
4.1 Создавайте классы, которые имеют высокую степень сплоченности (высокие связанные обязанности):
Возьмем этот пример:
Низкий класс связности:
Класс Автомобиль со следующими методами имеет низкую связность:
- Изменить параметры автомобиля
- Обновить цены
- Запланировать техническое обслуживание
- Отправить напоминание о техническом обслуживании
- Выберите финансирование
- Рассчитать ежемесячный платеж
Вы можете видеть, что класс делает вещи, связанные с обслуживанием и финансами транспортных средств, лучший способ организовать ваши классы — сделать их более сплоченными:
Автомобиль
- Изменить параметры автомобиля
- Обновить цены
Техническое обслуживание автомобиля
- Запланировать техническое обслуживание
- Отправить напоминание о техническом обслуживании
Автокредит
- Выберите финансирование
- Рассчитать ежемесячный платеж
Теперь, когда вы видите, у нас есть более высокий уровень сплоченности, разбивая класс на разные классы.
4.2. Избегайте очень общих классов, поскольку они менее связны и привлекают ленивых разработчиков:
Когда у вас есть очень общий класс, он привлекает ленивых разработчиков к добавлению кода поверх него, например:
Мои функции
Это плохо, потому что это приглашает каждого ленивого разработчика писать здесь свой код.
4.3. Избегайте очень маленьких классов. Наличие двух классов, которые интенсивно вызывают методы друг друга, является признаком, на который следует обратить внимание:
Когда у вас есть два класса, которые активно вызывают друг друга, возможно, это признак того, что они должны быть одним классом, опять же, вы редко сталкиваетесь с этим, но просто руководствуйтесь своим здравым смыслом, когда считаете, что класс слишком мал.
4.4. Избегайте примитивной одержимости и абстрагируйтесь от классов структур данных:
Грязно:
private saveUser(String firstName, String lastName, String state, String phone )
Очистить:
private void saveUser (User user)
Опять же, написание класса для пользователя помогает читателю концептуализировать его и способствует сопровождению, поскольку при добавлении нового параметра сигнатура метода не изменится.
4.5. Следуйте принципу близости, стремясь к тому, чтобы код читался сверху вниз, и объединяйте связанные действия:
Когда читатель читает ваш код, он читает его сверху вниз, вы часто видите, что какая-то функция вызывает другую функцию, в этом случае попробуйте следовать принципу близости, размещая функции, которые связаны друг с другом, понятно, что функции вызываются из разных мест, но по возможности держите связанные функции в классе вместе, давайте возьмем этот пример:
Очистить:
private void validateCandidate(Candidate canidate) { validateCandidateData (canidate); if (!candidateMeetsRequirements()) { throw new CandidateDoesntMeetRequirementsException(" The Candidate Doesn't meet our standards. ") ; } approaveInterviews(); } private void validateCandidateData (Candidate candidate) { int MIN_YEARS_OF_EXPERIENCE = 5; if (canidate.yearsOfExperience < MIN_YEARS_OF_EXPERIENCE ) throw new ExperienceYearsNotMetException(" The candidate does not have enough years of experience"); if (!canidated.isAwareOfCleanCodePrinciples ) throw new CanidateDoesntPracticeCleanCodeException ; } private bool candidateMeetsRequirements (){ return canidateHasExceptionalCV () || !noObviousRedFlags(); }
В предыдущем примере вы заметили, что функция validateCandidate вызывает функцию validateCandidateData и как легко и удобно читателю было читать и проверять код.
4.6 Стремитесь к правилу структуры: свернутый код должен читаться как структура:
Опять же, картинка стоит тысячи слов:
Вернемся к нашей метафоре организации кода в виде книги и, следуя принципу близости, попытайтесь применить ту же концепцию в своем коде, держите связанные функции вместе, свернутый код должен читаться как контуры.
5: Комментарии:
Последняя часть головоломки чистого кода — это комментарии, как упоминалось ранее, старайтесь отдавать предпочтение самодокументируемому коду, а не комментариям, используйте комментарии ТОЛЬКО тогда, когда кода самого по себе недостаточно.
Теперь мы поговорим о плохих комментариях и хороших комментариях.
5.1 Плохие комментарии:
В категории плохих комментариев больше, чем хороших!
5.1.1 Избегайте лишних комментариев.
Они просто избыточны, и лучше предположить, что читатель может читать и знать этот конкретный язык программирования, давайте возьмем несколько примеров:
Грязно:
int x = 1; // set x to 1 // default constructer public User() { } // calculate total price private void calculateTotalPrice(){ // total price should be calculated }
5.1.2 Избегайте комментариев с намерением:
Скорее всего, их можно объяснить в коде, используя рекомендации и принципы, которые мы обсуждали ранее! давайте возьмем пример:
Грязно:
// make sure the user account is deactivated if (user.status == 2)
Очистить:
if (user.status == Status.Inactive)
5.1.3 Избегайте комментариев с извинениями:
Пожалуйста, не извиняйтесь, исправьте это перед фиксацией/слиянием.
Грязно:
// I was too tired to refactor this
5.1.4 Избегайте предупреждающих комментариев:
Пожалуйста, не комментируйте с предупреждениями, исправьте это перед фиксацией/слиянием.
Грязно:
// See Paul to understand this code
5.1.5 Код убийства зомби:
Зомби-код — это закомментированный код, он не скомпилирован и не является частью производственного кода, но это все еще код, преследующий нас при чтении кода, это зомби-код, который следует убить. В основном это происходит в двух случаях:
- Неприятие риска: когда вы считаете, что этот код слишком опасен для удаления
- Накопление менталитета: когда вы думаете, что этот код может быть полезен для читателей (Подсказка: это не так)
Пожалуйста, удалите зомби-код, он шумный и увеличивает отношение сигнал-шум, если код использовался ранее, и вы боитесь его удалить, он может быть повторно использован снова, удалите его, вы можете получить его из системы управления версиями, и если вы не используя систему управления версиями, пожалуйста, начните ее использовать.
5.1.6. Избегайте комментариев-разделителей, они в основном указывают на то, что ваш код слишком много делает:
Разделительные комментарии — это комментарии, которые используются для разделения различных частей логики, вы можете подумать, что это хорошо, но этого можно избежать, разбив длинный код на более мелкие функции. Возьмем пример:
Грязно:
private void myVeryLongFunction(){ lots of code // start verifying the candidate verify the candidate // end of verifying thecandidate some more code
5.1.7 Избегайте комментариев Brace Tracker:
Иногда вы обнаружите, что используете оператор if для проверки некоторого условия, если условие истинно, вы хотите выполнить какую-то другую логику, а иногда эта логика становится слишком длинной, и вам нужно писать комментарии, чтобы отслеживать конец ваших операторов, особенно если у вас есть разные слои из них. Это плохой комментарий, и его можно избежать. Возьмем пример:
Грязно:
if (isValidLogin) { //do //lots //of things //to // log // user //in } // end of user login
Его очиститель, если вы извлечете эту логику входа в его функцию on, Clean:
if (isValidLogin) { loginUser(); }
5.1.8. Избегайте раздутых заголовков:
Иногда вы находите какие-то ненужные заголовки в верхней части файлов, эти заголовки описывают автора этого файла, дату создания файла и некоторые другие ненужные комментарии, будьте СУХИМ, эту информацию уже можно найти в системе контроля версий, и до тех пор, пока вы можете найти эту информацию, не повторяйте ее. Если заголовок необходим, обратите внимание на соглашения о том, как писать заголовок для этого конкретного языка. Возьмем пример:
Грязно:
//***************************************************** // Filename: MyAwesomeClass.Java * // * // Auther : Jin Foong * // * // Weather : It was very cold, and it was a public * // holiday ! * //*****************************************************
5.1.9 Избегайте комментариев в журнале дефектов:
Если вы когда-нибудь исправите ошибку исключения с нулевой точкой, а она была, не пишите комментарии, чтобы описать ситуацию, места для этого — тикеты и контроль версий, а не код. Возьмем пример:
Грязно:
// Bug 187 // we weren't checking for null if (username != null ){ //do something }
Хорошие комментарии:
Хватит негатива, комментарии иногда бывают хорошими.
Мы можем разделить хорошие комментарии на три категории:
5.2.1 Можно писать комментарии //TO DO:
Комментарии к выполнению могут быть очень полезны, когда вы хотите сделать что-то позже, их можно написать, но когда вы их пишете, лучше, если вы и ваша команда согласны с некоторыми стандартами и некоторыми формулировками, чтобы их было легко найти или чтобы их было легко найти. некоторые инструменты, которые могут найти их для вас, если вы будете следовать некоторым стандартам. Возьмем пример:
Очистить:
// TO DO : refactor out duplication
5.2.2 Можно писать сводные комментарии, только если это невозможно выразить в коде:
В редких случаях кода недостаточно, чтобы передать намерение, и вы можете захотеть написать сводку высокого уровня, которая может быть неясна из кода, в этом случае можно написать сводные комментарии.
// generate custume emails
5.2.3 Можно писать комментарии к документации, только если это невозможно выразить в коде:
Иногда код или API, которые вы используете, могут быть немного сложными и не могут быть выражены в коде, в этом случае может быть хорошей идеей иметь источник документации в ваших комментариях.
// see www.facebook.com/api // some code that is unique to this api
Теперь мы знаем все о хороших и плохих комментариях, и в заключение, прежде чем писать комментарии, задайте себе следующие вопросы:
- Могу ли я выразить это в коде? Если да, то, пожалуйста, сделайте это и будьте СУХИМИ.
- Я просто объясняю плохой код вместо рефакторинга? Если да, то сделайте рефакторинг.
- Должно ли это быть просто сообщением в фиксации системы управления версиями? Если да, то, пожалуйста, будьте СУХИМИ.
Теперь все части головоломки собраны, и, надеюсь, это может дать вам большинство направлений, необходимых для написания чистого кода.
ВАЖНО: Прежде, чем The End, я хотел бы упомянуть несколько замечаний:
Я написал эту статью так, что разделил ее на разделы, чтобы охватить разные области, большинство руководств по чистому коду представлены в отдельном, четком, пронумерованном формате, и цель этого состоит в том, чтобы, если вы наткнулись на эту статью и делаете обзоры кода, вы можете просто сослаться на статью автора кода, указав номер руководства. например, если вы видите, что у автора много кода с глубокими отступами, вы можете сослаться на рекомендации с номерами 3.2.1 и 3.2.2.
Кроме того, Кодекс — это ответственность. Чем меньше, тем лучше. Если вы найдете эту статью полезной, поделитесь ею со своими коллегами, товарищами по команде и друзьями. Давайте стремиться к тому, чтобы все разработчики писали чистый код.
Кредиты на Чистый код Pluralsight, курс написания кода для людей. Это вдохновило меня на написание этой статьи.