Роберт Мартин - Чистый код. Создание, анализ и рефакторинг
- Название:Чистый код. Создание, анализ и рефакторинг
- Автор:
- Жанр:
- Издательство:Питер
- Год:2019
- Город:СПб.
- ISBN:978-5-4461-0960-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Роберт Мартин - Чистый код. Создание, анализ и рефакторинг краткое содержание
Эта книга посвящена хорошему программированию. Она полна реальных примеров кода. Мы будем рассматривать код с различных направлений: сверху вниз, снизу вверх и даже изнутри. Прочитав книгу, вы узнаете много нового о коде. Более того, вы научитесь отличать хороший код от плохого. Вы узнаете, как писать хороший код и как преобразовать плохой код в хороший.
Книга состоит из трех частей. В первой части излагаются принципы, паттерны и приемы написания чистого кода; приводится большой объем примеров кода. Вторая часть состоит из практических сценариев нарастающей сложности. Каждый сценарий представляет собой упражнение по чистке кода или преобразованию проблемного кода в код с меньшим количеством проблем. Третья часть книги – концентрированное выражение ее сути. Она состоит из одной главы с перечнем эвристических правил и «запахов кода», собранных во время анализа. Эта часть представляет собой базу знаний, описывающую наш путь мышления в процессе чтения, написания и чистки кода.
Примечание верстальщика:
Чистый код. Создание, анализ и рефакторинг - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Более того, функции должны быть еще короче, чем в листинге 3.2! На деле листинг 3.2 следовало бы сократить до листинга 3.3.
public static String renderPageWithSetupsAndTeardowns(
PageData pageData, boolean isSuite) throws Exception {
if (isTestPage(pageData))
includeSetupAndTeardownPages(pageData, isSuite);
return pageData.getHtml();
}
Блоки и отступы
Из сказанного выше следует, что блоки в командах if, else, while и т.д. должны состоять из одной строки, в которой обычно содержится вызов функции. Это не только делает вмещающую функцию более компактной, но и способствует документированию кода, поскольку вызываемой в блоке функции можно присвоить удобное содержательное имя.
Кроме того, функции не должны содержать вложенных структур, так как это приводит к их увеличению. Максимальный уровень отступов в функции не должен превышать одного-двух. Разумеется, это упрощает чтение и понимание функций.
Правило одной операции

Совершенно очевидно, что функция из листинга 3.1 выполняет множество операций. Она создает буферы, производит выборку данных, ищет унаследованные страницы, строит пути, присоединяет загадочные строки, генерирует код HTML… и это еще не все. С другой стороны, в листинге 3.3 выполняется всего одна простая операция: включение в тестовую страницу начальных и конечных блоков.
Следующий совет существует в той или иной форме не менее 30 лет.
ФУНКЦИЯ ДОЛЖНА ВЫПОЛНЯТЬ ТОЛЬКО ОДНУ ОПЕРАЦИЮ. ОНА ДОЛЖНА ВЫПОЛНЯТЬ ЕЕ ХОРОШО. И НИЧЕГО ДРУГОГО ОНА ДЕЛАТЬ НЕ ДОЛЖНА.
Проблема в том, что иногда бывает трудно определить, что же считать «одной операцией». В листинге 3.3 выполняется одна операция? Легко возразить, что в нем выполняются минимум три операции:
1. Функция проверяет, является ли страница тестовой страницей.
2. Если является, то в нее включаются начальные и конечные блоки.
3. Для страницы генерируется код HTML.
Так как же? Сколько операций выполняет функция — одну или три? Обратите внимание: три этапа работы функции находятся на одном уровне абстракции под объявленным именем функции. Ее можно было бы описать в виде короткого TO [13] В языке LOGO ключевое слово TO использовалось так же, как в Ruby и Python используется «def». Таким образом, каждая функция начиналась со слова «TO».
-абзаца:
• TO RenderPageWithSetupsAndTeardowns, мы проверяем, является ли страница тестовой, и если является — включаем начальные и конечные блоки. В любом случае для страницы генерируется код HTML.
Если функция выполняет только те действия, которые находятся на одном уровне под объявленным именем функции, то эта функция выполняет одну операцию. В конце концов, функции пишутся прежде всего для разложения более крупной концепции (иначе говоря, имени функции) на последовательность действий на следующем уровне абстракции.
Вполне очевидно, что листинг 3.1 содержит множество различных действий на разных уровнях абстракции. Поэтому в нем явно выполняется более одной операции. Даже листинг 3.2 содержит два уровня абстракции; это доказывается тем, что нам удалось его сократить. С другой стороны, осмысленно сократить листинг 3.3 очень трудно. Команду if можно вынести в функцию с именем includeSetupsAndTeardownsIfTestPage, но это простая переформулировка кода без изменения уровня абстракции.
Итак, чтобы определить, что функция выполняет более одной операции, попробуйте извлечь из нее другую функцию, которая бы не являлась простой переформулировкой реализации [G34].
Секции в функциях
Взгляните на листинг 4.7 на с. 98. Обратите внимание: функция generatePrimes разделена на секции (объявления, инициализация, отбор). Это очевидный признак того, что функция выполняет более одной операции. Функцию, выполняющую только одну операцию, невозможно осмысленно разделить на секции.
Один уровень абстракции на функцию
Чтобы убедиться в том, что функция выполняет «только одну операцию», необходимо проверить, что все команды функции находятся на одном уровне абстракции. Легко убедиться, что листинг 3.1 нарушает это правило. Некоторые из его концепций — например, getHtml() — находятся на очень высоком уровне абстракции; другие (скажем, String pagePathName = PathParser.render(pagePath)) — на среднем уровне. Наконец, третьи — такие, как .append("\n") — относятся к чрезвычайно низкому уровню абстракции.
Смешение уровней абстракции внутри функции всегда создает путаницу. Читатель не всегда понимает, является ли некоторое выражение важной концепцией или второстепенной подробностью. Что еще хуже, при их смешении функция постепенно начинает обрастать все большим количеством второстепенных подробностей.
Чтение кода сверху вниз: правило понижения
Код должен читаться как рассказ — сверху вниз [KP78, p. 37].
За каждой функцией должны следовать функции следующего уровня абстракции. Это позволяет читать код, последовательно спускаясь по уровням абстракции в ходе чтения списка функций. Я называю такой подход «правилом понижения».
Сказанное можно сформулировать и иначе: программа должна читаться так, словно она является набором TO-абзацев, каждый из которых описывает текущий уровень абстракции и ссылается на последующие TO-абзацы следующего нижнего уровня.
• Чтобы включить начальные и конечные блоки, мы сначала включаем начальные блоки, затем содержимое тестовой страницы, а затем включаем конечные блоки.
• Чтобы включить начальные блоки, мы сначала включаем пакетные начальные блоки, если имеем дело с пакетом тестов, а затем включаем обычные начальные блоки.
• Чтобы включить пакетные начальные блоки, мы ищем в родительской иерархии страницу SuiteSetUp и добавляем команду include с путем к этой странице.
• Чтобы найти в родительской иерархии…
Опыт показывает, что программистов очень трудно научить следовать этому правилу и писать функции, остающиеся на одном уровне абстракции. Тем не менее освоить этот прием очень важно. Он играет ключевую роль для создания коротких функций, выполняющих только одну операцию. Построение кода по аналогии с набором последовательных TO-абзацев — эффективный метод поддержания единого уровня абстракции.
Взгляните на листинг 3.7 в конце этой главы. В нем приведен полный код функции testableHtml, переработанной в соответствии с описанными здесь принципами. Обратите внимание на то, как каждая функция «представляет» читателю следующую функцию и как каждая функция остается на едином уровне абстракции.
Команды switch
Написать компактную команду switch довольно сложно [14] Разумеется, сюда же относятся и длинные цепочки if/else.
. Даже команда switch всего с двумя условиями занимает больше места, чем в моем представлении должен занимать один блок или функция. Также трудно создать команду switch, которая делает что-то одно — по своей природе команды switch всегда выполняют N операций. К сожалению, обойтись без команд switch удается не всегда, но по крайней мере мы можем позаботиться о том, чтобы эти команды были скрыты в низкоуровневом классе и не дублировались в коде. И конечно, в этом нам может помочь полиморфизм.
Интервал:
Закладка: