Чистый код. Часть 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-я. Это большой список признаков, что код необходимо отрефакторить. В ней признаки сгруппированы по типам: комментарии, зависимости, функции и т. д.