Продолжая использовать наш сайт, вы даете согласие на обработку файлов cookie, которые обеспечивают правильную работу сайта. Благодаря им мы улучшаем сайт!
Принять и закрыть

Читать, слущать книги онлайн бесплатно!

Электронная Литература.

Бесплатная онлайн библиотека.



Главная
Все книги
Назад
Читать: Экстремальное программирование. Разработка через тестирование - Кент Бек на бесплатной онлайн библиотеке Э-Лит


Помоги проекту - поделись книгой:

}

(Хорошо, что в главе 4 мы упростили тест для Dollar. Благодаря этому работа по редактированию теста существенно упростилась. Похоже, в данной книге дела идут довольно гладко, однако я не могут гарантировать, что в будущем все будет так же хорошо.)

Теперь нам надо получить зеленую полоску. Какой способ будет самым простым? Проще всего скопировать код класса Dollar и заменить Dollar на Franc.

Стоп. Подождите-ка. Я уже вижу, как некоторые наиболее ярые сторонники правильных подходов начинают морщиться и плеваться. Повторное использование кода путем его дублирования через буфер обмена? Пренебрежение абстракцией? А как же все эти разговоры об основополагающих принципах ООП и чистом дизайне?

Если вам не по себе, глубоко вдохните через нос, досчитайте до трех и медленно выдохните через рот. Вам лучше? Теперь вспомните, что наш цикл состоит из пяти этапов. Иногда последовательное выполнение всех этапов занимает всего несколько секунд, однако в любом случае мы обязательно выполняем каждый из них:

1. Написать тест.

2. Добиться его безошибочной компиляции.

3. Запустить тест и убедиться, что он потерпел неудачу.

4. Добиться успешного выполнения теста.

5. Устранить дублирование.

На разных этапах решаются разные задачи, преследуются разные цели. То, что совершенно недопустимо для одного из этапов, может быть вполне приемлемым для другого этапа. Однако в целом методика TDD работает только в случае, если ни один из этапов не упущен. Если вы пропустите хотя бы одно звено, развалится вся цепочка.

Первые три фазы цикла разработки TDD должны выполняться как можно быстрее. Определяющая характеристика этих этапов – скорость. На этих этапах в жертву скорости можно принести очень многое, в том числе чистоту дизайна. Честно говоря, сейчас я несколько волнуюсь. Я только что разрешил вам забыть о принципах хорошего дизайна. Представляю, как вы приходите к своим коллегам, подчиненным и во всеуслышание объявляете: «Кент сказал, что все эти разговоры про хороший дизайн – полная ерунда!» Остановитесь. Цикл еще не закончен. Четырехногий уродец из благородного семейства пятиногих стульев вечно падает. Первые четыре шага нашего цикла не работают без пятого. Хороший дизайн в подходящее время! Сначала сделаем, чтобы код заработал, потом сделаем, чтобы код был правильным (make it run, make it right).

Теперь мне стало легче. Теперь я уверен, что до тех пор, пока вы не избавитесь от дублирования, вы не покажете свой код никому за исключением своего партнера по паре. На чем мы остановились? Ах, да. Забываем о принципах хорошего дизайна в угоду скорости (мы будем заниматься искуплением этого греха на протяжении нескольких следующих глав).

Franc

class Franc {

private int amount;

Franc(int amount) {

this.amount = amount;

}

Franc times(int multiplier) {

return new Franc(amount * multiplier);

}

public boolean equals(Object object) {

Franc franc = (Franc) object;

return amount == franc.amount;

}

}

$5 + 1 °CHF = $10, если курс обмена 2:1

$5 * 2 = $10

Сделать переменную amount закрытым (private) членом

Побочные эффекты в классе Dollar?

Округление денежных величин?

equals()

hashCode()

Равенство значению null

Равенство объектов

5 CHF * 2 = 1 °CHF

Дублирование Dollar/Franc

Общие операции equals()

Общие операции times()

Чтобы запустить код, нам не потребовалось прикладывать каких-либо усилий, поэтому мы смогли «перепрыгнуть» через этап «добиться безошибочной компиляции кода» (Make it compile).

Зато теперь в нашем коде полно повторяющихся фрагментов. Прежде чем приступить к разработке следующего теста, мы должны избавиться от дублирования. Думаю, что следует начать с обобщения метода equals(). Однако об этом в следующей главе. На текущий момент мы можем вычеркнуть из нашего списка еще один пункт, однако вместе с этим нам придется добавить в него два дополнительных пункта.

В данной главе мы

• решили отказаться от создания слишком большого теста и вместо этого создали маленький, чтобы обеспечить быстрый прогресс;

• создали код теста путем бесстыдного копирования и редактирования;

• хуже того, добились успешного выполнения теста путем копирования и редактирования разработанного ранее кода;

• дали себе обещание ни в коем случае не уходить домой до тех пор, пока не устраним дублирование.

6. Равенство для всех, вторая серия

$5 + 1 °CHF = $10, если курс обмена 2:1

$5 * 2 = $10

Сделать переменную amount закрытым (private) членом

Побочные эффекты в классе Dollar?

Округление денежных величин?

equals()

hashCode()

Равенство значению null

Равенство объектов

5 CHF * 2 = 1 °CHF

Дублирование Dollar/Franc

Общие операции equals()

Общие операции times()

В книге Crossing to Safety автор Вэлленц Стегнер (Wallance Stegner) описывает рабочее место одного из персонажей. Каждый инструмент находится на предназначенном для него месте, пол чисто вымыт и подметен, повсюду превосходный порядок и чистота. Однако чтобы добиться подобного положения вещей, персонаж не делал никаких специальных подготовительных процедур. «Подготовка была делом всей его жизни. Он подготавливался, затем убирался на рабочем месте». (Конец книги заставил меня громко рассмеяться в бизнес-классе трансатлантического «Боинга-747». Так что если решите ее прочитать, читайте с осторожностью.)

В главе 5 мы добились успешного выполнения теста. Это значит, что требуемая функциональность реализована. Однако чтобы сделать это быстро, нам пришлось продублировать огромный объем кода. Теперь пришло время убрать за собой.

Мы можем сделать так, чтобы один из разработанных нами классов стал производным от другого. Я попробовал сделать это, однако понял, что в этом случае ничего не выигрываю. Вместо этого удобнее создать суперкласс, который станет базовым для обоих разработанных нами классов. Ситуация проиллюстрирована на рис. 6.1. (Я уже пробовал так поступить и пришел к выводу, что это именно то, что нужно, однако придется приложить усилия.)


Рис. 6.1. Общий суперкласс для двух разработанных нами классов

Для начала попробуем реализовать в базовом классе Money общий для обоих производных классов метод equals(). Начнем с малого:

Money

class Money

Запустим тесты – они по-прежнему выполняются. Конечно же, мы пока не сделали ничего такого, что нарушило бы выполнение наших тестов, однако в любом случае лишний раз запустить тесты не помешает. Теперь попробуем сделать класс Dollar производным от класса Money:

Dollar

class Dollar extends Money {

private int amount;

}

Работают ли тесты? Работают. Можем двигаться дальше. Перемещаем переменную amount в класс Money:

Money

class Money {

protected int amount;

}

Dollar

class Dollar extends Money {

}

Режим видимости переменной amount потребовалось изменить: теперь вместо private используем модификатор доступа protected. В противном случае подкласс не сможет обратиться к этой переменной. (Если бы мы хотели двигаться еще медленнее, мы могли бы на первом шаге объявить переменную в классе Money, а на втором шаге удалить ее объявление из класса Dollar, однако я решил действовать смело и решительно.)

Теперь можно переместить код метода equals() вверх по иерархии классов, то есть в класс Money. Прежде всего мы изменим объявление временной переменной:



Поделиться книгой:



Регистрация