Написание чистого кода — цель каждого программиста. «Чистый код» указывал на то, что для того, чтобы писать хороший код, вы должны сначала знать, что такое грязный код и что такое чистый код, а затем, благодаря длительной преднамеренной практике, вы действительно сможете писать чистый код.
WTF/мин — единственная мера качества кода, и дядя Боб называет в книге плохой код бродягой, что только подчеркивает, что мы — жертвы плохого кода. В Китае есть более подходящий термин: Шишань, хоть и не очень изящный, но более объективный, программисты одновременно и жертвы, и преступники.
О том, что такое чистый код, в книге дается краткое изложение мастеров:
- Бьерн Страуструп: элегантный и эффективный; прямо к делу; уменьшить количество зависимостей; делать одну вещь хорошо
- Grady book: просто и понятно
- Дэйв Томас: Читабельно, ремонтопригодно, модульное тестирование
- Рон Джеффрис: никакого дублирования, единственная ответственность, экспрессивность
Среди них мне больше всего нравится описание выразительности, которое, кажется, выражает суть хорошего кода: описать функцию кода простым и прямым способом, не больше и не меньше.
В этой статье записаны некоторые личные «глубокие симпатии» или «просвещенные» взгляды после прочтения «чистого кода».
Искусство именования
Откровенно говоря, нейминг — штука сложная, и нужно много потрудиться, чтобы придумать подходящее имя, особенно когда наш родной язык не английский, который обычно используется в языках программирования. Но оно того стоит, правильное именование делает ваш код более интуитивно понятным и выразительным.
Хороший нейминг должен обладать следующими характеристиками:
1.1 Соответствует своему названию
Хорошие имена переменных говорят вам, что это такое, почему оно существует и как его использовать.
Если вам нужно объяснить переменные с помощью аннотаций, вы должны быть менее достойны этого имени.
Вот пример кода из книги, который показывает, как именование улучшает качество кода.
# bad code
def getItem(theList):
ret = []
for x in theList:
if x[0] == 4:
ret.append(x)
return ret
# good code
def getFlaggedCell(gameBoard):
'''扫雷游戏,flagged: 翻转'''
flaggedCells = []
for cell in gameBoard:
if cell.IsFlagged():
flaggedCells.append(cell)
return flaggedCells
1.2 Не вводите в заблуждение
- Не продавайте собачье мясо
- Не закрывать идиоматические сокращения
Код, который мне приходилось видеть в первые два дня до этого, на самом деле использует L как имя переменной, а User — это на самом деле список (одиночный мультиплекс не учится!!)
1.3 Значимые различия
Код написан для выполнения машиной и чтения людьми, поэтому концепции должны быть дифференцированы.
# bad
def copy(a_list, b_list):
pass
# good
def copy(source, destination):
pass
1.4 Использование прочитанных слов
Если имя не произносится, то дискуссия будет как в дураках
1.5 Используйте удобные для поиска имена
Длина имени должна соответствовать размеру области его действия.
1.6 Избегайте интеллект-карт
Например, если вы пишете временный код в коде, то читатель должен переводить слово в его реальное значение каждый раз, когда он его видит.
2. Примечания
Выразительный код не требует комментариев: правильное использование комментариев должно компенсировать нашу неспособность выразить себя в коде.
Надлежащая роль комментариев состоит в том, чтобы компенсировать наши неудачи в выражении наших намерений в коде, что звучит разочаровывающе, но это так. Правда в коде, аннотации — это просто информация из вторых рук, и самая большая проблема с аннотациями заключается в том, что они не синхронизированы или не эквивалентны.
Книга дает очень яркий пример: используйте для объяснения код, а не комментарии.
bad
// check to see if the employee is eligible for full benefit
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))
good
if (employee.isEligibleForFullBenefits())
Поэтому, когда вы хотите добавить комментарии, подумайте, можете ли вы изменить наименование или изменить уровень абстракции функции (кода), чтобы показать назначение кода.
Конечно, вы не можете отказаться от еды из-за того, что поперхнулись.В книге отмечается, что следующие ситуации являются хорошими заметками.
- Легальная информация
- Примечание о намерениях, почему
- предупреждение
- Примечания к TODO
- Важность преувеличения кажущегося иррациональным
Среди них я больше всего согласен с пунктами 2 и 5. Легко выразить то, что вы делаете, назвав это, но почему вы это делаете, не интуитивно понятно, особенно когда речь идет о профессиональных знаниях и алгоритмах. Кроме того, некоторый код, который поначалу кажется «не таким элегантным», может иметь особые пожелания, поэтому такой код следует комментировать, объясняя, почему он такой, например, для повышения производительности критических путей он может пожертвовать частью код читабельность.
Худший вид комментариев — это устаревшие или неправильные комментарии, что является огромной медвежьей услугой для сопровождающего кода (возможно, самого себя через несколько месяцев).К сожалению, кроме проверки кода, нет простого способа обеспечить код и комментарии. Синхронизировать.
3. Функция
3.1 Единая ответственность за функции
Функция должна делать только одну вещь, которая должна четко отображаться в имени функции. Метод суждения очень прост: посмотреть, может ли функция еще разбить другую функцию.
Функция либо делает do_sth, либо запрашивает, какой query_sth. Самое отвратительное, что имя функции означает только query_sth, а по факту будет do_sth, из-за чего функция имеет побочные эффекты. пример в книге
public class UserValidator {
private Cryptographer cryptographer;
public boolean checkPassword(String userName, String password) {
User user = UserGateway.findByName(userName);
if (user != User.NULL) {
String codedPhrase = user.getPhraseEncodedByPassword();
String phrase = cryptographer.decrypt(codedPhrase, password);
if ("Valid Password".equals(phrase)) {
Session.initialize();
return true;
}
}
return false;
}
}
3.2 Уровень абстракции функций
У каждой функции есть уровень абстракции, и операторы в функции должны быть на одном уровне абстракции, и разные уровни абстракции не могут быть помещены вместе. Например, если мы хотим поместить слона в холодильник, это должно выглядеть так:
def pushElephantIntoRefrige():
openRefrige()
pushElephant()
closeRefrige()
Три строки кода в функции описывают на одном уровне (высоте) три шага, относящиеся к порядку помещения слона в холодильник. Очевидно, что шаг pushElephant может содержать много подшагов, но на уровне pushElephantIntoRefrige нет необходимости знать слишком много деталей.
Когда мы хотим понять новый проект, читая код, мы обычно применяем стратегию «сначала вширь», читая код сверху вниз, сначала понимая общую структуру, а затем погружаясь в интересующие детали. Без хорошего абстрагирования деталей реализации (и сгущения в функцию, достойную названия) читатель может легко заблудиться в море деталей.
В какой-то степени это тоже очень похоже на принцип пирамиды.
Каждый уровень должен демонстрировать представления уровня выше него, и он также нуждается в поддержке следующего уровня; множественные аргументы между одним и тем же уровнем должны быть упорядочены в некоторой логической связи. pushElephantIntoRefrige — это центральный аргумент, который требует поддержки нескольких подэтапов, и между этими подэтапами существует логическая последовательность.
3.3 Функциональные параметры
Чем больше параметров у функции, тем больше входных условий можно комбинировать, тем больше требуется тестовых случаев и тем больше проблем.
По сравнению с возвращаемым значением, выходной параметр трудно понять. Я чувствую то же самое. Выходной параметр действительно неинтуитивен. С точки зрения вызывающей функции возвращаемое значение можно увидеть с первого взгляда, в то время как выходные параметры трудно идентифицировать. Выходные параметры обычно вынуждают вызывающую сторону проверять сигнатуру функции, что действительно недружественно.
Передача логического значения (называемого в книге аргументом флага) в функцию обычно является плохой идеей. Особенно, когда поведение после перехода в True или False — это не две стороны одного и того же, а две разные вещи. Это явно нарушает ограничение единой ответственности функций, и решение простое, то есть использовать две функции.
3.4 Dont repear yourself
На функциональном уровне добиться повторного использования проще всего и интуитивно понятнее. Многие IDE также сложны для того, чтобы помочь нам реорганизовать фрагмент кода в функцию.
Однако на практике тоже будет такая ситуация: кусок кода используется в нескольких методах, но это не совсем то же самое, если абстрагироваться в общую функцию, то нужно добавить параметры и добавить, если еще отличить. Это немного неудобно и кажется поддающимся рефакторингу, но не идеальным.
Некоторые из вышеперечисленных проблем вызваны тем, что этот код также нарушает принцип единой ответственности и делает более одной вещи, что затрудняет повторное использование.Решение заключается в подразделении метода для лучшего повторного использования. Также рассмотрите метод шаблона для обработки различий.
4. Тест
Жаль, что в проектах, через которые я проходил, тестированию (особенно модульному тестированию) никогда не уделялось должного внимания, и я не пробовал TDD. Именно из-за отсутствия я еще больше чувствую ценность хорошего теста.
Мы часто говорим, что хороший код должен быть удобочитаемым, поддерживаемым и расширяемым, а хороший код и архитектура нуждаются в постоянном рефакторинге и итерациях, но автоматизированное тестирование — это основа для обеспечения всего этого, и нет большого охвата. , регрессионное тестирование, никто не осмеливается модифицировать код и может только дать ему гнить.
Даже если юнит-тесты пишутся для основных модулей, то в целом очень небрежно, думая, что это всего лишь тестовый код, не достойный статуса производственного кода, думая, что до тех пор, пока он может проработать. Это приводит к очень плохой читабельности и ремонтопригодности тестового кода, что затрудняет обновление и развитие тестового кода вместе с производственным кодом и, наконец, приводит к сбою тестового кода. Таким образом, грязный тест эквивалентен отсутствию теста.
Итак, тестируйте код на три элемента: удобочитаемость, удобочитаемость, удобочитаемость.
Принципы и рекомендации по тестированию следующие:
- Вам не разрешено писать какой-либо производственный код, если только он не предназначен для прохождения неудачного модульного теста.
- Вам не разрешается писать модульный тест больше, чем этого достаточно для провала, а сбои компиляции — это сбои.
- Вам не разрешено писать производственный код больше, чем достаточно для прохождения одного неудачного модульного теста.
ПЕРВЫЕ рекомендации по тестированию:
- Быстрые тесты должны быть достаточно быстрыми, чтобы их можно было максимально автоматизировать.
- Независимые тесты должны быть независимыми. не зависеть друг от друга
- Повторяемость Тесты должны повторяться в любой среде.
- Самопроверяющиеся тесты должны иметь логический вывод. Не судите, пройден тест или нет, глядя на журнал как на неэффективный способ.
- Своевременные тесты должны быть написаны вовремя, до их соответствующего производственного кода.
Эта статья проходитopenwrite.cn/Создание инструмента и массовое распространение.