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



Expression sum = five.plus(five);

Bank bank = new Bank();

Money reduced = bank.reduce(sum, "USD");

assertEquals(Money.dollar(10), reduced);

}


Раньше, если у нас имелась «поддельная» реализация, для нас было очевидным, как можно вернуться назад и сформировать реальную реализацию. Для этого достаточно было заменить константы переменными. Однако в данном случае пока не понимаю, как вернуться назад. Поэтому, несмотря на некоторый риск, я решаю двигаться вперед:


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

$5 + $5 = $10

Операция $5 + $5 возвращает объект Money


Прежде всего, метод Money.plus() должен возвращать не просто объект Money, а реальное выражение (Expression), то есть сумму (Sum). (Возможно, в будущем мы оптимизируем специальный случай сложения двух одинаковых валют, однако это произойдет позже.)


Итак, в результате сложения двух объектов Money должен получиться объект класса Sum:


public void testPlusReturnsSum() {

Money five = Money.dollar(5);

Expression result = five.plus(five);

Sum sum = (Sum) result;

assertEquals(five, sum.augend);

assertEquals(five, sum.addend);

}


(Вы когда-нибудь слышали, что в английском языке первое слагаемое обозначается термином augend, а второе слагаемое – термином addend? Об этом не слышал даже автор до тех пор, пока не приступил к написанию данной книги.)

Только что написанный тест, скорее всего, проживет недолго. Дело в том, что он сильно связан с конкретной реализацией разрабатываемой нами операции и мало связан с видимым внешним поведением этой операции. Однако, заставив его работать, мы окажемся на шаг ближе к поставленной цели. Чтобы скомпилировать тест, нам потребуется класс Sum с двумя полями: augend и addend:


Sum

class Sum {

Money augend;

Money addend;

}


В результате получаем исключение преобразования классов (ClassCastException) – метод Money.plus() возвращает объект Money, но не объект Sum:


Money

Expression plus(Money addend) {

return new Sum(this, addend);

}


Класс Sum должен иметь конструктор:


Sum

Sum(Money augend, Money addend) {

}


Кроме того, класс Sum должен поддерживать интерфейс Expression:


Sum

class Sum implements Expression


Наша система компилируется, однако тесты терпят неудачу – это из-за того, что конструктор класса Sum не присваивает значений полям (мы могли бы создать «поддельную» реализацию, инициализировав поля константами, однако я обещал двигаться быстрее):


Sum

Sum(Money augend, Money addend) {

this.augend = augend;

this.addend = addend;

}


Теперь в метод Bank.reduce() передается объект класса Sum. Если суммируются две одинаковые валюты и целевая валюта совпадает с валютой обоих слагаемых, значит, результатом будет объект класса Money, чье значение будет равно сумме значений двух слагаемых: