Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

Книги: [Классика] [Базы данных] [Internet/WWW] [Сети] [Программирование] [UNIX] [Windows] [Безопасность] [Графика] [Software Engineering] [ERP-системы] [Hardware]

     

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

Бек К.

Издано: Издательский дом "Питер"
ISBN: 5804600516
Мягкий переплет, 224 стр.

Начало
Cодержание
Отрывок
[Заказать книгу в магазине "Мистраль"]

Отрывок

Обмен валюты

$5 + 10 CHF = $10, если курс обмена 2:1
$5 + $5 = $10
Операция $5 + $5 возвращает объект Money
Bank.reduce(Money)
Приведение объекта Money с одновременной конверсией валют
Reduce(Bank,String)

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

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() эквивалентность элементов массива?

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 + 10 CHF = $10, если курс обмена 2:1
$5 + $5 = $10
Операция $5 + $5 возвращает объект Money
Bank.reduce(Money)
Приведение объекта Money с одновременной конверсией валют
Reduce(Bank,String)

Далее мы переходим к нашему последнему, самому большому тесту, $5 + 10 CHF. В данной главе мы применили несколько важных технологий:
  -добавили параметр, который, как мы ожидаем, нам понадобится;
  -удалили дублирование между кодом и тестами;
  -написали тест (testArrayEquals), чтобы проверить порядок функционирования встроенной операции Java;
  -создали вспомогательный закрытый (private) класс, не обладающий собственными тестами;
  -допустили ошибку при рефакторинге, написали еще один тест для того, чтобы изолировать проблему.

Начало
Cодержание
Отрывок
[Заказать книгу в магазине "Мистраль"]

 

Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

VPS в России, Европе и США

Бесплатная поддержка и администрирование

Оплата российскими и международными картами

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...