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



Bank.reduce(Money)

Приведение объекта Money с одновременной конверсией валют

Reduce(Bank,String)


Изменения, перемены, обмены – их объятия заслуживают внимания (особенно если у вас есть книга с фразой в заголовке «в объятиях изменений» (embrace change))[8]. Впрочем, нас заботит простейшая форма обмена – у нас есть два франка и мы хотим получить один доллар. Это звучит как готовый тест:


public void testReduceMoneyDifferentCurrency() {

Bank bank = new Bank();

bank.addRate("CHF", "USD", 2);

Money result = bank.reduce(Money.franc(2), "USD");

assertEquals(Money.dollar(1), result);

}


Когда я конвертирую франки в доллары, я просто делю значение на два (мы по-прежнему игнорируем все эти неприятные проблемы, связанные с дробными числами). Чтобы сделать полоску зеленой, мы добавляем в код еще одну уродливую конструкцию:


Money

public Money reduce(String to) {

int rate = (currency.equals("CHF") && to.equals("USD"))

? 2

: 1;

return new Money(amount / rate, to);

}


Получается, что класс Money знает о курсе обмена. Это неправильно. Единственным местом, в котором выполняются любые операции, связанные с курсом обмена, должен быть класс Bank. Мы должны передать параметр типа Bank в метод Expression.reduce(). (Вот видите? Мы так и думали, что нам это потребуется. И мы оказались правы.) Вначале меняем вызывающий код:


Bank

Money reduce(Expression source, String to) {

return source.reduce(this, to);

}


Затем меняем код реализаций:


Expression

Money reduce(Bank bank, String to);


Sum

public Money reduce(Bank bank, String to) {

int amount = augend.amount + addend.amount;

return new Money(amount, to);

}


Money

public Money reduce(Bank bank, String to) {

int rate = (currency.equals("CHF") && to.equals("USD"))

? 2

: 1;

return new Money(amount / rate, to);

}


Методы должны быть общедоступными (public), так как все методы интерфейсов должны быть общедоступными (я надеюсь, можно не объяснять, почему).

Теперь мы можем вычислить курс обмена внутри класса Bank:


Bank

int rate(String from, String to) {

return (from.equals("CHF") && to.equals("USD"))

? 2

: 1;

}


И обратиться к объекту bank с просьбой предоставить значение курса обмена:


Money

public Money reduce(Bank bank, String to) {

int rate = bank.rate(currency, to);

return new Money(amount / rate, to);

}


Эта надоедливая цифра 2 снова отсвечивает как в разрабатываемом коде, так и в теле теста. Чтобы избавиться от нее, мы должны создать таблицу обменных курсов в классе Bank и при необходимости обращаться к этой таблице для получения значения обменного курса. Для этой цели мы могли бы воспользоваться хеш-таблицей, которая ставит в соответствие паре валют соответствующий обменный курс. Можем ли мы в качестве ключа использовать двухэлементный массив, содержащий в себе две валюты? Проверяет ли метод Array.equals() эквивалентность элементов массива?