9 крышесносных правил для тех, кто хочет писать чистый код
Они взорвали мой мозг, и я мгновенно стал программировать лучше.
zaidi razak / Shutterstock
Вы слышали про объектную гимнастику (Object Calisthenics)? Если нет, то вам точно стоит её освоить! Тем более что это всего-то девять правил.
Я рассказывал о них многим разработчикам, и все они реагировали схоже. Сначала думали, что это шутка: «Как вообще можно хоть какой-то код написать по этим правилам?» Но, поприменяв их, обычно соглашались, что код и правда становится чище — причём почти мгновенно.
Так что же это?
Понятие «объектная гимнастика» ввёл Джефф Бэй (Jeff Bay) в своей книге «The ThoughtWorks Anthology» (на русском не издавалась. — Пер.). Так он назвал группу упражнений на объектно-ориентированное программирование.
Применяя объектную гимнастику, можно сделать код:
- читабельнее;
- удобнее:
• для отладки,
• тестирования,
• повторного использования
• и сопровождения.
Считайте это упражнением. Вам нужно научиться выполнять эти правила при кодинге. А вот в повседневном программировании какими-то из них всегда можно пожертвовать — если следовать им слишком сложно или результат того не стоит.
Итак, выберите себе кодерскую задачку, начните её решать и постарайтесь строго следовать этим принципам. Даже если некоторые из них поначалу кажутся глупыми, просто продолжайте.
1. Только один уровень вложенности в каждом методе
То есть одно значение отступа на весь метод. Так его легче будет читать.
Вам придётся извлечь и вынести в отдельные методы некоторые фрагменты кода — например, условия и циклы. Этим новым методам нужно будет дать говорящие имена и вызвать их из исходного метода.
В чём профит
- выполняется принцип единственной ответственности (SRP);
- имена становятся понятнее,
- методы — короче,
- и их удобнее использовать повторно.
2. Не используйте else
Вложенные условия усложняют код, делают его нелинейным.
Как обойтись без else
Мы решили слегка отойти от оригинальной статьи и добавили пояснения к каждому способу. — Пер.
- Использовать значения по умолчанию.
Например, ваша программа работает с объектами-фруктами: яблоками и грушами. По конвейеру движутся коробки с фруктами. Некоторые коробки подписаны, некоторые — нет. Если коробка не подписана, то вы считаете, что в ней лежат яблоки, — ну, так договорились.
В этом случае вы можете написать что-то вроде:
если коробка подписана, считать, что внутри те фрукты, которые указаны на коробке
иначе считать, что там яблоки
А ещё вы можете по умолчанию считать, что в коробке яблоки, и менять это значение, только если встретите надпись «груши».
- Практиковать ранний возврат из метода.
Этот способ подойдёт для тривиального случая.
Вместо:
если условие1 выполняй действие1
иначе выполняй действие2
можно написать:
если условие1 выполняй действие1 и вернись
а ниже внутри того же метода расположить обработку второго условия — ведь исполнение дойдёт до него, только если не сработает условие1.
- Выносить ветвления кода в отдельные методы.
В этом случае методы будут просто вызываться друг за другом, а проверки условий окажутся у них внутри. Если условие не выполнится, программа вернётся на уровень выше и вызовет следующий метод со следующей проверкой.
- Применить полиморфизм.
Полиморфизм — один из столпов ООП. Благодаря ему один и тот же метод может по-разному реализовываться в иерархии классов.
Здесь автор говорит о том, что конструкцию с несколькими условиями иногда бывает уместно заменить вызовом одного метода.
Так получится сделать, если создать систему классов-наследников, в которых по-разному определить этот один метод.
- Задействовать шаблон проектирования «Состояние».
Шаблон проектирования «Состояние» полезен, когда в зависимости от состояния системы одно и то же действие (переход к следующему этапу) должно обрабатываться по-разному.
Например, кофейный автомат сначала ждёт выбора напитка, потом оплаты, а только после этого готовит кофе.
Метод перехода на следующий шаг можно написать с кучей проверок типа «если сейчас я на шаге N, то делай то-то», а можно создать классы для каждого состояния и прописать действия по переходу на следующий шаг в них.
- Применить шаблон проектирования «Стратегия».
Шаблон проектирования «Стратегия» определяет семейство схожих алгоритмов и помещает каждый из них в отдельный класс.
В вызывающем классе хранится ссылка на стратегию, которую сейчас нужно использовать. Так что вместо множества проверок типа «если..., то...» вызывается один метод класса-стратегии.
В чём профит
- код не дублируется,
- становится проще
- и читается легче.
3. Оборачивайте примитивные типы
Оборачивая примитивные типы в классы, мы инкапсулируем (скрываем) тип. Если позже в ходе рефакторинга мы захотим изменить примитивные типы, это можно будет сделать в одном месте.
А ещё такой код легче воспринимать, ведь по сигнатуре объекта-обёртки сразу ясно, что передавать методу в качестве параметров.
Примечание переводчика
В оригинальной книге правило звучит так: «оборачивайте примитивы и строки».
Например, если метод принимает параметр типа int, это мало о чём говорит. Другое дело, если тип параметра будет, скажем, Hour. Мы оборачиваем целое число (часов) в класс. В тот же класс можно добавить проверку допустимых значений, и тогда никто не сможет передать в метод 36 или другое неподходящее число.
В чём профит
- Соблюдается инкапсуляция.
- Появляются подсказки для типов.
- Можно изолировать схожее поведение в отдельных методах и использовать их повторно.
4. По одной точке в строке
Это следствие закона Деметры: «Общайся только с друзьями» (речь об общении между классами с помощью вызова методов друг друга. — Пер.).
Точку вы используете для вызова методов в Java или С#. Вызовы типа object.getProperty().getSubProperty().doSomething() — это очень плохая практика! Классы не должны знать так много деталей реализации других классов.
В чём профит
- Соблюдается инкапсуляция.
- Выполняется принцип открытости/закрытости.
5. Не сокращайте имена
Вы когда-нибудь встречали странное сокращённое название метода или переменной. Такое, что назначение их без контекста можно понять пятью разными способами?
Если вам приходится сокращать название метода, возможно, этот метод делает больше, чем должен. То есть нарушен принцип единственной ответственности. Поразмыслите над этим.
Поразмыслили? А теперь не сокращайте наименования!
В чём профит
- Соблюдается SRP.
- Вы избегаете путаницы.
- Нет дублирования кода.
6. Не раздувайте классы
Делайте их небольшими:
- 15–20 строк на метод,
- 50 строк на класс,
- 10 классов на пакет.
В конце концов, если ваш класс специализируется на чём-то одном, он не должен быть большим, верно?
В чём профит
- Соблюдается SRP.
- Модули уменьшаются.
- Код становится хорошо согласованным (coherent).
7. Не больше двух переменных экземпляра (instance variable) на класс
Один класс должен иметь дело с одним состоянием, максимум с двумя. Так что, если в классе более двух переменных экземпляра, возможно, он нарушает принцип единственной ответственности.
Примечание переводчика
Переменная экземпляра (instance variable, атрибут) — переменная, которая хранит свойство объекта — экземпляра класса. Этим она отличается от статической переменной (относится к классу, а не к его экземпляру) и от локальной переменной, которая объявляется в членах класса — например, в методах.
Кажется, это требование очень сложно выполнить. Уверен, вы видели классы с десятками параметров в конструкторе, так ведь?
Отлично! Почему бы не сгруппировать их в один объект? Или ещё раз хорошенько подумать о том, точно ли у этого класса идеальная архитектура и не делает ли он слишком много лишнего.
В чём профит
- В модулях поддерживается сильная связность (high cohesion).
- Соблюдается инкапсуляция.
- Становится меньше зависимостей между различными сущностями программы.
8. Первоклассные коллекции
Это похоже на правило №3, только применительно к коллекциям.
Внутри вашего класса может быть по-прежнему простая коллекция. Но, обернув её в класс, вы в будущем сможете легко провести рефакторинг: например, изменить тип коллекции.
Примечание переводчика
В книге The ThoughtWorks Anthology автор акцентирует внимание на том, что в таком классе — обёртке над коллекцией вообще не должно быть других членов, кроме этой коллекции. Зато туда можно добавить методы фильтрации или добавления/удаления элементов.
В чём профит
- Появляются более функциональные классы-обёртки над тривиальными коллекциями.
- Остаётся только одно место, которое отвечает за поведение коллекции, — удобно, если нужно что-то исправить.
- Соблюдается инкапсуляция.
9. Никаких геттеров и сеттеров
Не принимайте решений вне класса, позвольте ему самому заниматься своим делом. Иными словами, следуйте принципу «Рассказывай, а не спрашивай».
В чём профит
- Соблюдается принцип открытости/закрытости.
Что дальше?
Теперь вы знаете всё! Следовать этим принципам сперва будет сложно. Появится искушение всё бросить. Но воспринимайте это как вызов себе — соберитесь и упражняйтесь (никаких оправданий!).
А вот строго выполнять все эти правила в ежедневной разработке вовсе не обязательно. Помните про компромиссные варианты и выбирайте то, что лучше.