Четыре совета по написанию «чистого кода»

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

Несколько лет назад у нас были огромные проблемы с качеством создаваемого кода. Сегодня оно значительно улучшилось благодаря целенаправленным усилиям.

Мы прочитали «Чистый код» Роберта Мартина и сделали все возможное, чтобы выполнить его рекомендации. Внедрение практики «чистого кода» в долгосрочной перспективе удвоит производительность (с минимальными затратами).

Из всех идей, взятых из «Чистого кода», которые мы реализовали, пять основных дали 80% прироста производительности команды.

1. «Если код не протестирован, то он нерабочий».

Проводите много тестов, особенно модульных.

2. Используйте правильные имена.

Применяйте короткие и точные имена для переменных, классов и функций.

3. Классы и функции должны быть небольшими и соответствовать принципу единичной ответственности (SRP).

Функции должны состоять не более чем из четырех строк, а классы не более чем из 100 строк. Они также должны делать одну и только одну вещь.

4. Функции не должны иметь побочных действий.

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

Рассмотрим каждый из этих пунктов.

Содержание

1. «Если код не протестирован, то он нерабочий».

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

2. Используйте правильные имена.

В «Компьютерной науке» есть две серьезные проблемы: невалидный кэш и именование.

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

Нельзя называть переменную data, foobar или myNumber, а класс — SomethingManager. Убедитесь, что используемые имена являются коротки и точны. Оптимизируйте разработку и упростите поиск файлов с помощью ярлыков IDE «Найти по имени». Уделяйте именованию первоочередное внимание.

3. Классы и функции должны быть небольшими и соответствовать принципу единичной ответственности (SRP).

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

Рассмотрим пример. В Node есть модуль npm под названием «build-url», который делает то, что предполагает его имя: он создает URL-адреса. Исходник файла, который мы рассмотрим, находится здесь. Ниже приведен соответствующий код.

function buildUrl(url, options) {
var queryString = [];
var key;
var builtUrl;

if (url === null) {
builtUrl = '';
} else if (typeof(url) === 'object') {
builtUrl = '';
options = url;
} else {
builtUrl = url;
}

if (options) {
if (options.path) {
builtUrl += '/' + options.path;
}

if (options.queryParams) {
for (key in options.queryParams) {
if (options.queryParams.hasOwnProperty(key)) {
queryString.push(key + '=' + options.queryParams[key]);
}
}
builtUrl += '?' + queryString.join('&');
}

if (options.hash) {
builtUrl += '#' + options.hash;
}
}

return builtUrl;
};

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

function buildUrl(url, options) {
  const baseUrl = _getBaseUrl(url);
  const opts = _getOptions(url, options);

  if (!opts) {
    return baseUrl;
  }

  urlWithPath = _appendPath(baseUrl, opts.path);
  urlWithPathAndQueryParams = _appendQueryParams(urlWithPath, opts.queryParams)
  urlWithPathQueryParamsAndHash = _appendHash(urlWithPathAndQueryParams, opts.hash);

  return urlWithPathQueryParamsAndHash;
};

function _getBaseUrl(url) {
  if (url === null || typeof(url) === 'object') {
    return '';
  }
  return url;
}

function _getOptions(url, options) {
  if (typeof(url) === 'object') {
    return url;
  }
  return options;
}

function _appendPath(baseUrl, path) {
  if (!path) {
    return baseUrl;
  }
  return baseUrl += '/' + path;
}

function _appendQueryParams(urlWithPath, queryParams) {
  if (!queryParams) {
    return urlWithPath
  }

  const keyValueStrings = Object.keys(queryParams).map(key => {
    return `${key}=${queryParams[key]}`;
  });
  const joinedKeyValueStrings = keyValueStrings.join('&');

  return `${urlWithPath}?${joinedKeyValueStrings}`;
}

function _appendHash(urlWithPathAndQueryParams, hash) {
  if (!hash) {
    return urlWithPathAndQueryParams;
  }
  return `${urlWithPathAndQueryParams}#${hash}`;
}

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

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

a) Инициализировать базовый URL-адрес и параметры.

b) Добавить путь (если он есть).

c) Добавьте параметры запроса (если он есть).

d) Добавьте хэш (если он есть).

Каждый из этих шагов преобразуется в под функцию.

Перейдем к другой связанной концепции — принципу единичной ответственности. Что это значит? Цитата из Википедии:

Принцип единичной ответственности (SRP) — это принцип ООП, согласно которому каждый модуль или класс должен отвечать за одну часть функционала, предоставляемого программой. Реализация должна быть инкапсулирована в одном классе.

Роберт Мартин в «Чистом коде» дает другое определение:

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

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

4. Функции не должны иметь побочного действия

Посмотрите пример, приведенный ниже. Вы заметили побочное действие?

function getUserByEmailAndPassword(email, password) {
let user = UserService.getByEmailAndPassword(email, password);
if (user) {
LoginService.loginUser(user); // Log user in, add cookie (Side effect!!!!)
}
return user;
}

Функция, как следует из ее имени, предназначена для поиска пользователя по email и паролю. Но она имеет скрытое побочное действие: регистрирует пользователя, который создает токен входа. Функция добавляет его в базу данных и отправляет cookie обратно пользователю со значением logged in.

Это неправильно по ряду причин. Интерфейс функции трудно понять без прочтения кода реализации. Даже если побочное действие входа задокументировано. Разработчики из-за простого имени функции не подумают, что им нужно прочитать документацию. Они будут использовать эту функцию исключительно для извлечения объекта user. При этом не понимая, что они добавляют cookie к запросу, что может привести к ошибкам.

Тестирование функции будет сложным, учитывая все зависимости. Проверка того, можете ли вы найти пользователя по адресу электронной почты / паролю, требует эмуляции HTTP-ответа, а также обработки записей в таблице токенов входа.

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

Запомните и применяйте эти четыре принципа «чистого кода», чтобы повысить производительность своей команды разработчиков.

Данная публикация представляет собой перевод статьи «These four “clean code” tips will dramatically improve your engineering team’s productivity» , подготовленной дружной командой проекта Интернет-технологии.ру

Меню