img

Расшифровка «чистого кода» - практическое введение в принципы написания «чистого кода» для начинающих

 

«Любой дурак может написать код, который поймет компьютер. Хорошие программисты пишут код, который поймут люди», - Мартин Фаулер. 

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

В этой статье мы рассмотрим самые важные принципы повышения качества кода, и я также покажу вам несколько примеров.

Большую часть примеров я взял из книги Роберта Дж. Мартина «Чистый код». Это классика программирования, и я бы рекомендовал вам прочитать ее полностью, когда у вас будет время. 

Как давать имена переменным (и прочее)

«Есть только две сложные вещи в компьютерных науках: аннулирование элементов кэша и присвоение имен сущностям», - Фил Карлтон.

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

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

Как создавать смысловые имена

Не используйте комментарии, чтобы пояснить, для чего нужна переменная. Если имя было создано так, что оно требует комментария, то вам следует еще раз подумать и переименовать ее, а не писать комментарий. 

«Имя должно говорить о том, для чего переменная существует, что она делает и как ее использовать. Если имя требует комментария, то оно не отображает ее смысла», - «Чистый код».

Плохой вариант:

var d; // elapsed time in days

Я видел такой вариант написания не раз. Это всеобщее заблуждение думать, что вы можете скрыть свою неразбериху с помощью комментариев. Не используйте такие буквы, как x, y, a или b, в качестве имен переменных, только если для этого нет веской причины (исключением здесь являются переменные цикла).

Хороший вариант:

var elapsedTimeInDays;
var daysSinceCreation;
var daysSinceModification;

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

Не допускайте дезинформации

Будьте внимательными со словами, у которых есть конкретное значение. Не нужно называть группу учетных записей accountList, если на самом деле ее тип не имеет никакого отношения к списку. Слово имеет определенное значение, и это может привести к ошибочным заключениям.

Даже если это на самом деле список, то в любом случае лучше просто оставить accounts.

Плохой вариант:

var accountList = [];

Хороший вариант:

var accounts = []

Избегайте использования пропускаемых слов

Пропускаемые слова – это слова, которые не несут никакой дополнительной информации о переменной. Они являются излишними, и их стоит удалить.

Ниже приведен список некоторых популярных пропускаемых слов:

  • the (префикс)
  • info
  • data
  • variable
  • object
  • manager

Если ваш класс называется UserInfo, то слово Info можно удалить, достаточно оставить User. И не нужно быть семь пядей во лбу, чтобы использовать BookData вместо Book в качестве имени класса, поскольку класс в любом случае хранит данные. 

Используйте легкопроизносимые имена

Если вы не можете произнести имя переменной, то вы не сможете обсуждать ее так, чтобы это не звучало нелепо. 

Плохой вариант:

const yyyymmdstr = moment().format("YYYY/MM/DD");

Хороший вариант:

const currentDate = moment().format("YYYY/MM/DD");

Используйте имена с возможностью поиска

Не используйте волшебные чисел в вашем коде. Отдайте предпочтение именованным константам с возможностью поиска. Не используйте односимвольные имена для констант, поскольку они могут много где встречаться, и, следовательно, их не просто будет найти. 

Плохой вариант:

if (student.classes.length < 7) {
   // Do something
}

Хороший вариант:

if (student.classes.length < MAX_CLASSES_PER_STUDENT) {
    // Do something
}

Такой вариант будет гораздо лучше, поскольку переменная MAX_CLASSES_PER_STUDENT может использоваться в нескольких местах кода. Если в дальнейшем нам потребуется заменить ее на 6, то мы сможем просто изменить константу. 

Глядя на плохой вариант, у читателя могут возникнуть вопросы, например, какова степень важности числа 7?

Вы также должны использовать соглашения о присвоении имен константам и их объявлении, такие как private static final в Java или const в JavaScript.

Будьте логичными

Всегда следуйте правилу: «одно слово на каждое концептуальное представление». Не нужно использовать fetchretrieve и get для одних и тех же операций в разных классах. Выберите что-то одно и используйте его на протяжении всего проекта, чтобы люди, сопровождающие кодовую базу, или клиенты вашего API могли с легкостью отыскать методы, которые им нужны. 

Как писать функции

Помните о том, что они должны быть небольшими

Функции должны быть небольшими, от слова совсем. Они редко бывают длиной более 20 строк. Чем длиннее функция, тем больше вероятность того, что она будет выполнять несколько действий и будет иметь побочные эффекты.

Убедитесь, что они выполняют только одно действие

«Функции должны выполнять только одно действие. И они должны делать это хорошо. И делать это должны только они», - «Чистый код».

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

Иногда бывает сложно понять, глядя на функцию, сколько действий она выполняет. Отличный способ узнать – попробовать выделить другую функцию с другим именем. Если у вас получится, то это значит, что это должна быть другая отдельная функция.  

Возможно, это самая важная концепция в данной статье, и пройдет какое-то время, прежде чем вы к ней привыкнете. Но как только вы набьете руку, ваш код будет выглядеть более полноценным, и его легче будет реорганизовывать, понимать и тестировать.

Инкапсулируйте условные конструкции в функциях

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

Ниже я привел фрагмент кода из моего школьного проекта. Он отвечает за добавление фишки на поле игры Connect 4. 

Метод isValidInsertion отвечает за проверку корректности номера столбца, и за счет этого мы можем уделить больше внимания логике добавления фишки.

public void insertChipAt(int column) throws Exception {
        if (isValidInsertion(column)) {
            insertChip(column);
            boardConfiguration += column;
            currentPlayer = currentPlayer == Chip.RED ? Chip.YELLOW : Chip.RED;
        } else {
            if (!columnExistsAt(column))
                throw new IllegalArgumentException();
            else if (isColumnFull(column - 1) || getWinner() != Chip.NONE)
                throw new RuntimeException();
        }
    }

Ниже я привел код для метода isValidInsertion (если вам интересно). 

    private boolean isValidInsertion(int column) {
        boolean columnIsAvailable = column <= NUM_COLUMNS && column >= 1 && numberOfItemsInColumn[column - 1] < NUM_ROWS;
        boolean gameIsOver = getWinner() != Chip.NONE;
        return columnIsAvailable && !gameIsOver;

Без этого метода условие if выглядело бы вот так:

if (column <= NUM_COLUMNS
 && column >= 1
 && numberOfItemsInColumn[column - 1] < NUM_ROWS 
 && getWinner() != Chip.NONE)

Ужасно, не правда ли? Я думаю, да.

Поменьше аргументов

У функций должно быть не более двух аргументов, чем меньше, тем лучше. По мере возможности не допускайте использование трех и более аргументов.

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

Не используйте аргументы типа флаг

Аргумент типа флаг – это логический аргумент, который передается функции. В зависимости от значения этого аргумента выполняется одно из двух действий. Допустим, у нас есть функция, которая отвечает за бронирование билетов на концерт и есть два вида пользователей: Premium и Regular. Код примерно следующий:

    public Booking book (Customer aCustomer, boolean isPremium) {
      if(isPremium) 
       // logic for premium book
      else
       // logic for regular booking
    }

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

Отсутствие побочных эффектов

Побочные эффекты – это непредусмотренные последствия работы вашего кода. Они могут менять переданные параметры, если вы передаете их по ссылке, или, быть может, даже глобальную переменную.

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

Ниже приведен пример из книги:

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; 
      }
}

Вы уже заменили побочный эффект этой функции?

Она проверяет пароль, но в случае, если пароль является действительным, она все равно инициализирует сеанс – это и есть побочный эффект.

Вы, конечно, можете поменять имя функции на что-то вроде checkPasswordAndInitializeSession, чтобы явно определить этот эффект. Но когда вы это сделаете, вы должны будете заметить, что на самом деле ваша функция выполняет два действия, и вам не стоит инициализировать сеанс в этой функции.

Не повторяйтесь

Повторяемый код – корень всех зол в программном обеспечении. Дублированный код подразумевает, что если появляются какие-то изменения в логике, то вам нужно будет внести изменения в нескольких местах, а это может поспособствовать появлению ошибок.

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

extract_method-1024x576

Выделение метода в IntelliJ

Бонус

Не оставляйте код в комментариях

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

Поэтому просто удалите его, даже если он был важен. Вы всегда сможете его найти – для этого есть управление версиями. 

Узнайте соглашения вашего языка

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

Например, в Java следует использовать camelCase, а в Python – snake_case. В C# вы ставите открывающие фигурные скобки на новой строке, но в Java и JavaScript вы ставите их на ту же строку. 

Такие вещи могут отличаться в зависимости от языка, и какого-то универсального стандарта нет.

Заключение

Написание «чистого программного кода» - это не тот навык, которым можно овладеть за одну ночь. Это привычка, которую необходимо тренировать, соблюдая эти принципы и применяя их каждый раз, когда вы пишете код. 

 

Ссылка
скопирована
Программирование
Скидка 25%
Python Advanced. Продвинутый курс
Освойте асинхронное и метапрограммирование, изучите аннотацию типов и напишите собственное приложение на FastAPI. Улучшите свои навыки Python, чтобы совершить быстрый рост вашего грейда до уровня middle.
Получи бесплатный
вводный урок!
Пожалуйста, укажите корректный e-mail
отправили вводный урок на твой e-mail!
Получи все материалы в telegram и ускорь обучение!
img
Еще по теме:
img
За последние годы микрослужбы прошли путь от обычного переоцененного модного словечка до вещи, которую вы, как специалист по про
img
Введение Резидентные базы данных (или хранилища в памяти) по большей части делают упор на хранилище данных в памяти, а не на жес
img
  Многие люди рассуждают так: «зачем, ну зачем мне изучать еще один язык программирования?» Для них это лишнее, и они стараютс
img
Введение Объекты в Kubernetes – это базовые постоянные сущности, которые описывают состояние кластера Kubernetes. Модули – это э
img
  Ключевые отличия между JDK, JRE и JVM: JDK – это набор средств для разработки программного обеспечения, JRE – это програ
img
  Довольно часто мы встречаемся с компонентами архитектуры программного обеспечения, которые являются частью любой системы, но п
Комментарии
ЛЕТНИЕ СКИДКИ
40%
50%
60%
До конца акции: 30 дней 24 : 59 : 59