private String endingContext() {
int contextStart = expected.length() - suffixLength;
int contextEnd =
Math.min(contextStart + contextLength, expected.length());
return expected.substring(contextStart, contextEnd);
}
private String endingEllipsis() {
return (suffixLength > contextLength ? ELLIPSIS : "");
}
}
Результат выглядит вполне симпатично. Модуль делится на группы: первую группу составляют функции анализа, а вторую — функции синтеза. Функции топологически отсортированы таким образом, что определение каждой функции размещается перед ее первым использованием. Сначала определяются все функции анализа, а за ними следуют функции синтеза.
Внимательно присмотревшись, можно заметить, что я отменил некоторые решения, принятые ранее в этой главе. Например, некоторые извлеченные методы были снова встроены в formatCompactedComparison, а смысл выражения shouldNotBeCompacted снова изменился. Это типичная ситуация. Одна переработка часто приводит к другой, отменяющей первую. Переработка представляет собой итеративный процесс, полный проб и ошибок, но этот процесс неизбежно приводит к формированию кода, достойного настоящего профессионала.
Заключение
Итак, «правило бойскаута» выполнено: модуль стал чище, чем был до нашего прихода. И дело не в том, что он был недостаточно чист, — авторы отлично потрудились над ним. Однако не существует модуля, который нельзя было бы улучшить, и каждый из нас обязан оставить чужой код хотя бы немного лучше, чем он был.
Глава 16. Переработка SerialDate
Посетив страницу http://www.jfree.org/jcommon/index.php, вы найдете на ней описание библиотеки JCommon. Глубоко в недрах этой библиотеки скрыт пакет
Класс SerialDate написан Дэвидом Гилбертом (David Gilbert). Несомненно, Дэвид является опытным и компетентным программистом. Как вы сами убедитесь, в этом коде он проявил значительную степень профессионализма и дисциплины. Во всех отношениях это «хороший код». А сейчас я намерен разнести его в пух и прах.
Дело вовсе не в злом умысле. И я вовсе не считаю, что я намного лучше Дэвида и поэтому имею право критиковать его код. Действительно, если заглянуть в мой код, я уверен, что вы найдете в нем немало поводов для критики.
Нет, дело не в моем скверном характере или надменности. Я всего лишь намерен проанализировать код с профессиональной точки зрения, не более и не менее. Это то, что все мы должны делать спокойно и без угрызений совести. И все мы должны только приветствовать, когда такой анализ кто-то проводит за нас. Только после подобной критики мы узнаем нечто новое. Это делают врачи. Это делают пилоты. Это делают адвокаты. И мы, программисты, тоже должны этому научиться.
И еще одно замечание по поводу Дэвида Гилберта: Дэвид — не просто хороший программист. У него хватило смелости и доброй воли на то, чтобы бесплатно предоставить свой код сообществу. Дэвид разместил свой код в открытом доступе и предложил всем желающим использовать и обсуждать его. Отличная работа!
Класс SerialDate (листинг Б.1, с. 390) представляет даты в языке Java. Зачем нужен класс для представления дат, если в Java уже имеются готовые классы java.util.Date, java.util.Calendar и т.д.? Автор написал свой класс из-за проблемы, с которой часто сталкивался сам. Ее суть хорошо разъясняется в открывающем комментарии Javadoc (строка 67). Возможно, кому-то такое решение покажется радикальным, но мне и самому приходилось сталкиваться с этой проблемой, и я приветствую класс, ориентированный на работу с датой вместо времени.
Прежде всего — заставить работать
В классе SerialDateTests содержится набор модульных тестов (листинг Б.2, с. 411). Все тесты проходят. К сожалению, беглое изучение тестов показывает, что тестирование не покрывает часть кода [T1]. Например, поиск показывает, что метод MonthCodeToQuarter (строка 334) не используется [F4]. Соответственно он не включается в модульные тесты.
Итак, я запустил Clover, чтобы узнать, какая часть кода реально покрывается модульными тестами. Clover сообщает, что модульные тесты выполняют только 91 из 185 исполняемых команд SerialDate (около 50%) [T2]. Карта покрытия напоминала лоскутное одеяло, а по всему классы были разбросаны большие пятна невыполняемого кода.
Моей целью было полное понимание и переработка кода этого класса. Я не мог добиться этого без значительного улучшения тестового покрытия, поэтому мне пришлось написать собственный набор абсолютно независимых модульных тестов (листинг Б.4, с. 419).