TestSuite
def add(self, test):
self.test = test
self.tests.append(test)
Теперь мы используем коллекцию тестов вместо единичного теста. Исходя из существующего набора тестов данное преобразование можно считать рефакторингом (оно не нарушает семантику), так как в составе коллекции присутствует только один элемент.
TestSuite
def run(self, result):
for test in self.tests:
test.run(result)
Теперь можно удалить не используемую переменную экземпляра test:
TestSuite
def add(self, test):
self.tests.append(test)
Поэтапную миграцию данных можно использовать также при переходе между эквивалентными форматами, использующими различные протоколы, например, если речь идет о Java, при переходе от Vector/Enumerator к Collection/Iterator.
Выделение метода (Extract Method)
Как длинный сложный метод можно сделать простым для чтения? Выделите небольшую часть длинного метода в отдельный метод и обратитесь к этому методу из длинного метода.
Как
Выделение метода на самом деле является несколько более сложным атомарным рефакторингом. Здесь я опишу самый типичный случай. К счастью, многие среды разработки поддерживают автоматическое выполнение этого рефакторинга. Итак, чтобы выделить метод:
1. Определите фрагмент кода, который можно выделить в отдельный метод. Хорошими кандидатами являются тела циклов, сами циклы, а также ветви условных операторов.
2. Убедитесь, что внутри этого фрагмента не происходит присваивания значений временным переменным, объявленным вне области видимости, соответствующей этому фрагменту.
3. Скопируйте код из старого метода в новый. Скомпилируйте его.
4. Для каждой временной переменной или параметра первоначального метода, используемого в новом методе, добавьте параметр в новый метод.
5. Сделайте так, чтобы в нужном месте старый метод обращался к новому методу.
Зачем
Я использую «Выделение метода» (Extract Method), когда пытаюсь понять сложный код. «Значит так, этот кусок кода делает вот это. А этот кусок делает это. К чему мы там дальше обращаемся?» Через полчаса код будет выглядеть гораздо лучше, ваш партнер начнет понимать, что вы действительно оказываете ему помощь, а вы – существенно лучше понимать, что же все-таки происходит внутри кода.
Я использую выделение метода, чтобы избавиться от дублирования, когда вижу, что два метода обладают сходными участками кода. В этом случае я выделяю схожие участки в отдельный метод. (Браузер рефакторинга для Smalltalk – Refactoring Browser – выполняет еще более полезную задачу: он просматривает код в поисках метода, аналогичного коду, который вы намерены выделить, и в случае, если такой метод уже есть, предлагает использовать уже существующий метод вместо того, чтобы создавать новый.)
Разделение методов на множество мелких кусочков может зайти слишком далеко. Если я не вижу, куда идти дальше, я часто использую шаблон «Встраивание метода» (Inline Method), чтобы собрать код в одном месте и увидеть новый, более удобный способ разделения.
Встраивание метода (Inline Method)
Как можно упростить код, если становится сложно уследить за последовательностью передачи управления от метода к методу? Замените обращение к методу кодом этого метода.
Как
1. Скопируйте код метода в буфер обмена.
2. Вставьте код метода вместо обращения к методу.
3. Замените все формальные параметры фактическими. Если, например, вы передаете reader.getNext(), то есть выражение, обладающее побочным эффектом, будьте осторожны и присвойте полученное значение временной переменной.
Зачем
Один из моих рецензентов пожаловался на сложность кода в первой части книги, который требует от объекта Bank преобразовать объект Expression в объект Money.
public void testSimpleAddition() {
Money five = Money.dollar(5);
Expression sum = five.plus(five);
Bank bank = new Bank();
Money reduced = bank.reduce(sum, "USD");
assertEquals(Money.dollar(10), reduced);
}
«Это слишком сложно. Почему бы не реализовать преобразование в самом объекте Money?» Ну что же, поставим эксперимент. Как это сделать? Давайте встроим метод Bank.reduce() и посмотрим, как это будет выглядеть:
public void testSimpleAddition() {
Money five = Money.dollar(5);
Expression sum = five.plus(five);
Bank bank = new Bank();
Money reduced = sum.reduce(bank, «USD»);
assertEquals(Money.dollar(10), reduced);
}
Возможно, вторая версия понравится вам больше, возможно, нет. Важно понимать, что при помощи шаблона «Встраивание метода» (Inline Method) вы можете экспериментировать с последовательностью выполнения действий. Когда я выполняю рефакторинг, я формирую у себя в голове мысленную картину системы с кусками логики и потоком выполнения программы, перетекающим от одного объекта к другому объекту. Когда мне кажется, что я вижу нечто многообещающее, я использую рефакторинг, чтобы попробовать это и увидеть результат.
Читать дальше
Конец ознакомительного отрывка
Купить книгу