Чистый код. Часть 3

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

Глава 9. Юнит-тесты

Коротко:

  • тесты важны настолько же, насколько важен продакшн-код;
  • тесты должны быть прозрачными и понятными.

Три закона TDD:

  • писать продакшн-код можно после того, как будет написан отказной тест для этого кода;
  • объём кода для этого теста не должен быть больше, чем необходимый, чтобы тест отказал;
  • объём продакшн-кода не должен быть больше, чем необходимый, чтобы тест был пройден.

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

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

// плохо
const result = testedFunction()

it('should be okay', () => {
  expect(typeof result).toEqual('number')
  expect(result / Math.abs(result)).toEqual(1)
  expect(isFinite(result)).toEqual(true)
})

// хорошо
const result = testedFunction()

it('should return number', () => {
  expect(typeof result).toEqual('number')
})

it('should return positive number', () => {
  expect(result / Math.abs(result)).toEqual(1)
})

it('should return finite number', () => {
  expect(isFinite(result)).toEqual(true)
})

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

Глава 10. Классы

Коротко:

  • классы должны быть компактными;
  • название класса должно описывать его функциональность;
  • слабое зацепление — хорошо.

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

Название класса должно полно отображать, какие обязанности он выполняет. Имя же помогает определить обязанности класса. Слова наподобие Processor, Manager, Super намекают, что класс слишком большой, и его можно разбить. Идеально — когда класс можно описать 25 словами без использования «но», «если», «или».

Совокупность классов должна давать полную картину приложения, они должны дополнять друг друга.

Проектировать классы надо с учётом их развития в будущем. Внесение изменений не должно быть слишком трудным. Чтобы этого добиться, надо уменьшать связанность классов и модулей.

Главы 11–12. Системы и проектирование

Коротко:

  • разделяйте логику запуска от логики рантайма;
  • определите области ответственности на этапе архитектуры;
  • дизайн программной системы должен быть простым.

Программные системы должны разделять логику приложения при запуске от логики рантайма. Часто используют ленивую инициализацию:

const getService = () => {
  if (!service) service = new SomeService()
  return service
}

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

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

Дизайн (в плане проектирования программной системы) должен быть прозрачным и простым. Простой дизайн:

  • проходит все тесты;
  • не содержит дублирования;
  • отражает то, что хотел сделать программист;
  • содержит минимально-необходимое количество классов и компонентов.

Оставшиеся главы

Я рекомендую прочесть полностью самим.

В 13-й рассказывается о параллельных вычислениях, для веба не очень актуально, но интересно. В 16-й — о рефакторинге, когда его проводить, с чего начинать. Самая интересная — 17-я. Это большой список признаков, что код необходимо отрефакторить. В ней признаки сгруппированы по типам: комментарии, зависимости, функции и т.д.

Предыдущие части и ссылки по теме