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() эквивалентность элементов массива?
public void testArrayEquals() {
assertEquals(new Object[] {"abc"}, new Object[] {"abc"});
}
Нет. Тест провалился. Придется создавать специальный объект, который будет использоваться в качестве ключа хеш-таблицы:
Pair
private class Pair {
private String from;
private String to;
Pair(String from, String to) {
this.from = from;
this.to = to;
}
}
Мы планируем использовать объекты Pair в качестве ключей, поэтому нам необходимо реализовать методы equals() и hashCode(). Я не собираюсь писать для этого тесты, так как мы разрабатываем код в контексте рефакторинга. Дело в том, что от работоспособности этого кода жестко зависит успешное выполнение существующих тестов. Если код работает неправильно, существующие тесты потерпят неудачу. Однако если бы я программировал в паре с кем-то, кто плохо представлял бы себе направление дальнейшего движения, или если бы логика кода была более сложной, я несомненно приступил бы к разработке специальных тестов.
Pair
public boolean equals(Object object) {
Pair pair = (Pair) object;
return from.equals(pair.from) && to.equals(pair.to);
}
public int hashCode() {
return 0;
}
0 – ужасное хеш-значение, однако такой метод хеширования легко реализовать, стало быть, мы быстрее получим работающий код. Поиск валюты будет осуществляться простым линейным перебором. Позже, когда у нас будет множество валют, мы сможем тщательнее проработать этот вопрос, использовав реальные данные.
Теперь нам нужно место, в котором мы могли бы хранить значения обменных курсов:
Bank
private Hashtable rates= new Hashtable();
Нам также потребуется метод добавления нового курса обмена:
Bank
void addRate(String from, String to, int rate) {
rates.put(new Pair(from, to), new Integer(rate));
}
И метод, возвращающий обменный курс:
Bank
int rate(String from, String to) {
Integer rate = (Integer) rates.get(new Pair(from, to));
return rate.intValue();
}
Подождите-ка минутку! Перед нами красная полоса. Что случилось? Взглянув на код, мы видим, что проблема в неправильном значении курса при обмене доллара на доллары. Мы ожидаем, что при обмене USD на USD курс обмена будет равен 1, однако на текущий момент это не так. Поскольку эта ситуация стала для нас сюрпризом, оформим ее в виде дополнительного теста:
public void testIdentityRate() {
assertEquals(1, new Bank(). rate("USD", "USD"));
}
Теперь у нас три ошибки, однако все они могут быть исправлены при помощи одного небольшого изменения:
Bank
int rate(String from, String to) {
if (from.equals(to)) return 1;
Integer rate = (Integer) rates.get(new Pair(from, to));
return rate.intValue();
}
Зеленая полоска!
$5 + 1 °CHF = $10, если курс обмена 2:1
Операция $5 + $5 возвращает объект Money
Вильям Л Саймон , Вильям Саймон , Наталья Владимировна Макеева , Нора Робертс , Юрий Викторович Щербатых
Зарубежная компьютерная, околокомпьютерная литература / ОС и Сети, интернет / Короткие любовные романы / Психология / Прочая справочная литература / Образование и наука / Книги по IT / Словари и Энциклопедии