Читать книгу Экстремальное программирование. Разработка через тестирование - Кент Бек - Страница 7

Часть I
На примере денег
2. Вырождающиеся объекты

Оглавление

Обычный цикл разработки на основе тестирования состоит из следующих этапов:

1. Напишите тест. Представьте, как будет реализована в коде воображаемая вами операция. Продумав ее интерфейс, опишите все элементы, которые, как вам кажется, понадобятся.

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

3. Улучшите решение. Теперь, когда система работает, избавьтесь от прошлых огрехов и вернитесь на путь истинной разработки. Устраните дублирование, которое вы внесли, и быстро сделайте так, чтобы полоска снова стала зеленой.

Наша цель – чистый код, который работает (отдельное спасибо Рону Джеффризу за этот слоган). Иногда такой код не по силам даже самым лучшим программистам, и почти всегда он не достижим для большинства программистов (вроде меня). Разделяй и властвуй, приятель, – в этом весь смысл! Сначала мы напишем код, «который работает», после чего создадим «чистый код». Такой подход противоречит модели разработки на основе архитектуры, в которой вы сначала пишете «чистый код», а потом мучаетесь, пытаясь интегрировать в проект код, «который работает».


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

$5 * 2 = $10

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

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

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


Мы получили один рабочий тест, но в процессе заметили нечто странное: при выполнении операции с объектом Dollar изменяется сам объект. Хотелось бы написать так:


public void testMultiplication() {

Dollar five = new Dollar(5);

five.times(2);

assertEquals(10, five.amount);

five.times(3);

assertEquals(15, five.amount);

}


Я не могу представить простого способа, который заставит этот тест выполняться. После первого вызова метода times() пять уже больше не пять – на самом деле это уже десять. Если же метод times() будет возвращать новый объект, тогда мы сможем умножать наши исходные пять баксов хоть целый день, и они не изменятся. Для реализации этой идеи нам потребуется изменить интерфейс объекта Dollar и, соответственно, изменить тест. Это нормально, ведь вполне возможно, что наши догадки о правильном интерфейсе не более правдоподобны, чем догадки о правильной реализации.


public void testMultiplication() {

Dollar five = new Dollar(5);

Dollar product = five.times(2);

assertEquals(10, product.amount);

product = five.times(3);

assertEquals(15, product.amount);

}


Новый тест не будет компилироваться, пока мы не изменим объявление метода Dollar.times():


Dollar

Dollar times(int multiplier) {

amount *= multiplier;

return null;

}


Теперь тест компилируется, но не работает. И это тоже прогресс! Чтобы заставить его работать, придется возвращать новый объект Dollar с правильным значением:


Dollar

Dollar times(int multiplier) {

return new Dollar(amount * multiplier);

}

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

$5 * 2 = $10

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

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

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


В главе 1, когда мы заставляли тест работать, мы начинали с заготовки и постепенно улучшали код, пока он не стал полноценным. Теперь мы написали сразу правильную реализацию и молились, пока выполнялись тесты (довольно короткие молитвы, честно говоря – выполнение тестов занимает миллисекунды). Нам повезло, тесты выполнились успешно, и мы вычеркнули еще один пункт.

Экстремальное программирование. Разработка через тестирование

Подняться наверх