Заметки:
- Функция должна быть еще более компактная. Еще компактней. Максимально компактной.
- Желательно не делать функции больше 20 строк.
- Всякие if, else, while должны состоять из одной строчки и содержать вызов функции. С красивым именем это будет читаться как захватывающая история. Например:
if (book.isNotFinished) readBook() else startNewBook() - Старайтесь делать минимум вложенности. Максимум отступов — 2. По себе знаю, что это довольно сложно. Иногда, когда много логики и алгоритмов, выходит достаточно много отступов. :(
- Функция должна выполнять только одну операцию. Всегда.
- Одна операция — это когда на одном уровне абстракции (ui, алгоритмы, соединения строк, парсинг, БД — это все разные уровни).
- Как проверить, что функция выполняет больше одной операции? Попробовать извлечь из нее другую. Именно новую функцию, а не просто переименовать.
- Если функцию можно разделить на секции (объявление, инициализация, сортировка) — это больше одной операции.
- Код должен читаться как рассказ — сверху вниз. По мере чтения понижаем уровень абстракции.
- switch и длинные if — else if — else сложно сократить. Автор советует похоронить такие функции в абстрактной фабрике (https://refactoring.guru/ru/design-patterns/abstract-factory) и никому не показывать. Вот этот пункт для меня самый сложный. Я пока что не выношу switch.
- Команды из 10 пункта допустимы, если встречаются однократно, используются для создания полиморфных объектов и скрываются за отношениями наследования, чтобы остаться невидимыми для остальных частей системы.
- Содержательные имена! Глава два полностью посвящена именам. Там все описано. https://zen.yandex.ru/media/android_junior/chistyi-kod-konspekt-glava-2-soderjatelnye-imena-6064d9892a842913a70b0804
- В идеальном мире количество аргументов равно нулю. Функций с тремя аргументами надо избегать. Почему? Нам постоянно надо помнить о всех аргументах и читать код становится намного сложнее. Да и в тестировании покрыть тестами метод с несколькими аргументами сложно, потому что придется перебрать все доступные варианты.
- Флаги ужасны. Они сразу говорят, что функция выполняет минимум два действия. Не используйте их.
- Если функция должна принимать три и более аргументов, то подумайте над тем, чтобы упаковать их в класс. Раз эти аргументы идут в один метод, то, возможно, они вместе образуют какой-то объект.
- Для унарных функций обычно используется название в виде глагол и существительное: write(name) или writeField(name).
- Никаких скрытых эффектов, которые не очевидны из названия. Если функция называется checkPassword(), то там должен проверяться пароль. Не надо внутри функции очищать какие-нибудь базы или делать запросы на получение нового пароля. А то это будет внезапный сюрприз для разработчика, который захочет использовать метод проверки пароля.
- Никаких выходных аргументов. Если функция должна изменять состояние, то только состояние своего владельца, а не других объектов.
- Функция или что-то делет или отвечает на вопрос.
- Вместо кодов ошибок используйте исключения. Заодно это уменьшает вложенность.
- Try-catch выделяем в отдельные блоки. try { deletePage() } catch (exception) { }. Так и читать гораздо легче и сразу видно, что происходит в try, а что в catch.
- Раньше использовали такую вещь, как магнит зависимости — класс с кодами ошибки. Но при добавлении нового кода приходится перекомпилировать проект, поэтому исключения намного лучше. Я впервые услышала про такое. Но все равно интересно. :)
- Не дублируйте.
- Сначала пишем много и неуклюже — потом уменьшаем. Совет на все времена. У меня прям сегодня такое было, когда я изменила кучу классов, прокидывала флаги и хардкодила. Потом, когда разобралась, что и как работает, — потихоньку удаляла флаги, переносила и уменьшала код. В итоге получилась красота.