681 MGraphics.ru - На примере денег - Мультивалютные деньги
Уроки photoshopa


Экстремальное программирование - На примере денег

Мультивалютные деньги

Условия перепечатки материалов

Рейтинг статьи: 55555
Проголосовал 1 человек.
Оцените статью:

Мультивалютные деньги

Вначале мы рассмотрим объект, созданный Уордом для системы WyCash, — мультивалютные деньги. Допустим, у нас есть отчет вроде этого.

Что нам понадобится, чтобы сгенерировать такой отчет? Или, другими, словами, какой набор успешно проходящих тестов сможет гарантировать нам, что созданный код правильно генерирует отчет? Нам понадобится:

    выполнять сложение величин в двух различных валютах и конвертировать

результат с учетом указанного курса обмена;

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

           

Мы составим список задач, который будет напоминать нам о планах, не даст запутаться и покажет, когда все будет готово. В начале работы над задачей мы вы­делим ее полужирным шрифтом, вот так. Закончив работу над ней, мы ее вы­черкнем, вот так. Задумавшись о написании нового теста, мы добавим новую задачу в наш список.

Как видно из нашего списка задач, сначала мы займемся умножением. Итак, какой объект понадобится нам в первую очередь? Вопрос с подвохом. Мы начнем не с объектов, а с тестов. (Мне приходится постоянно напоминать себе об этом, так что я просто притворюсь, что вы также забывчивы, как и я.)

Попробуем снова. Итак, какой тест нужен нам в первую очередь? Если исходить из списка задач, первый тест представляется довольно сложным. Попробуем начать с малого — умножение, — сложно ли его реализовать? Займемся им для начала.

Когда мы пишем тест, мы воображаем, что у нашей операции идеальный ин­терфейс. Попробуем представить, как будет выглядеть наша операция снаружи. Конечно, наши представления не всегда будут находить воплощение, но в любом случае стоит начать с наилучшего возможного программного интерфейса (API) и при необходимости вернуться назад, чем сразу делать вещи сложными, уродливыми и «реалистичными».

Простой пример умножения:

public void testMultiplicationO {

Dollar five» new Dollar(5);

five.times(2);

assertEqualsd(10. five.amount);

 }

(Знаю, знаю: публичные поля, побочные эффекты, целые числа для денежных величин и все такое. Маленькие шаги — помните? Мы отметим, что где-то есть душок2, и продолжим дальше. У нас есть тест, который не выполняется, и мы хо­тим как можно скорее увидеть зеленую полоску.)

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

$5 * 2 = $10

Сделать переменную amount закрытым членом класса Побочные эффекты в классе Dollar? Округление денежных величин?

Тест, который мы только что создали, даже не компилируется, но это легко ис­править. (О том, когда и как создаются тесты, я расскажу позже — когда мы будем подробнее говорить о среде тестирования, JUnit.) Как проще всего заставить тест компилироваться (пусть он пока и не будет срабатывать)? У нас четыре ошибки компиляции:

    нет класса Dollar;

    нет конструктора;

    нет метода times(int);

    нет поля (переменной) amount

Устраним их, одну за другой. (Я всегда ищу некоторую численную меру прогресса.) От одной ошибки мы избавимся, определив класс Dollar

Dollar

class Dollar

Одной ошибкой меньше, осталось еще три. Теперь нам понадобится конструктор, причем совершенно, не обязательно, чтобы он что-то делал — лишь бы компилировался.

Dollar

Dollar(int amount) { }

Осталось две ошибки. Необходимо создать заглушку для метода times(). Снова мы выполним минимум работы, только чтобы заставить тест компилироваться:

Dollar

void timesdnt multiplier) {

}

Теперь осталась только одна ошибка. Чтобы от нее избавиться, нужно создать поле (переменную) amount.

Dollar

int amount:

Отлично! Теперь мы можем запустить тест и увидеть, что он не выполняется: ситуация продемонстрирована на рис. 1.1.

Загорается зловещий красный индикатор. Среда тестирования (JUnit в нашем случае) выполнила небольшой фрагмент кода, с которого мы начали, и выяснилось, что хотя мы ожидали результат «10» — вместо этого получили «0». Ужас­но...

Вовсе нет! Неудача — это тоже прогресс. Теперь у нас есть конкретная мера неудачи. Это лучше, чем просто догадываться, что у нас что-то не так. Наша задача «реализовать мультивалютность» превратилась в «заставить работать этот тест, а потом заставить работать все остальные тесты». Так намного проще и на­много меньше поводов для страха. Мы заставим этот тест работать.

Возможно, вам это и не понравится, но сейчас наша цель не получить идеальное решение, а заставить тест выполняться. Мы принесем свою жертву на алтарь истины и совершенства чуть позже.

                                   

Рис. 1.1. Прогресс! Тест не проходит

Наименьшее изменение, которое заставит тест успешно выполняться, пред­ставляется мне таким:

Dollar

int amount= 10:

           

Рис 1.2. Тест успешно выполняется

Рисунок 1.2 показывает результат повторного запуска теста. Теперь мы видим ту самую зеленую полоску, воспетую в поэмах и прославленную в веках.

           

Вот оно, счастье! Но радоваться рано, ведь цикл еще не завершен. Уж слиш­ком мал набор входных данных, которые заставят такую странно попахивающую, такую наивную реализацию работать правильно. Перед тем как двигаться даль­ше, немного поразмышляем.

Вспомним, полный цикл TDD состоит из следующих этапов:

1.   Добавить небольшой тест.

2.   Запустить все тесты, при этом обнаружить, что что-то не срабатывает.

3.   Внести небольшое изменение.

4.   Снова запустить тесты и убедиться, что все они успешно выполняются.

5.   Устранить дублирование с помощью рефакторинга.

ЗАВИСИМОСТЬ И ДУБЛИРОВАНИЕ -----------------------------------------------------------------------------------

Стив Фримен (Steve Freeman) указал, что проблема с тестами и кодом заключается не в дубли­ровании (на которое я еще не указал вам, но сделаю это, как только закончится отступление). Проблема заключается в зависимости между кодом и тестами — вы не можете изменить одно, не изменяя другого. Наша цель — иметь возможность писать новые осмысленные тесты, не меняя при этом код, что невозможно при нашей текущей реализации.

Зависимость является ключевой проблемой разработки программного обеспечения. Если фрагменты SQL, зависящие от производителя используемой вами базы данных, разбросаны по всему коду и вы хотите поменять производителя, то непременно окажется, что код зависит от этого производителя. Вы не сможете поменять производителя базы данных и при этом не изме­нить код.

Зависимость является проблемой, а дублирование — ее симптомом. Чаще всего дублирова­ние проявляется в виде дублирования логики — одно и то же выражение появляется в различ­ных частях кода. Объекты — отличный способ абстрагирования, позволяющий избежать дан­ного вида дублирования.

В отличие от большинства проблем в реальной жизни, где устранение симптомов приводит только к тому, что проблема проявляется в более страшной форме где-то еще, устранение дуб­лирования в программах устраняет и зависимость. Именно поэтому существует второе правило TDD. Устраняя дублирование перед тем, как заняться следующим тестом, мы максимизируем наши шансы сделать его успешным, внеся всего одно изменение.

Мы выполнили первые четыре пункта цикла, и все готово к устранению дуб­лирования. Но где же оно? Обычно мы замечаем дублирование в нескольких раз­ных фрагментах кода, однако в нашем случае — друг друга дублируют тест и тес­тируемый код. Еще не видите? Как насчет того, чтобы написать так:

Dollar

int amount= 5*2;

Теперь ясно, откуда мы взяли число 10. Видимо, мы в уме произвели умноже­ние, причем так быстро, что даже не заметили. Произведение «5 умножить на 2» присутствует как в тесте, так и в тестируемом коде. Только изначально в коде оно было представлено в виде константы 10. Сейчас же 5 и 2 отделены друг от друга, и мы должны безжалостно устранить дублирование перед тем, как двинуться дальше. Такие вот правила.

Действия, с помощью которого мы устранили бы 5 и 2 за один шаг, не сущест­вует. Но что если мы переместим установку поля (переменной) amount из ини­циализации объекта в метод timesQ?

                       

Dollar

int amount:

void timesdnt multiplier) {

amount= 5*2: }

Тест все еще успешно выполняется, а индикатор остался зеленым. Успех нам пока сопутствует.

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

Оставим рассуждения. На чем мы остановились? Ну да, мы избавлялись от дублирования между кодом теста и рабочим кодом. Где мы можем взять 5? Это значение передавалось конструктору, поэтому мы можем сохранить его в пере­менной amount:

Dollar

Dollardnt amount) {

this.amount= amount: }

А если мы сохранили его в переменной amount, то сможем использовать это значение в методе timesQ:

Dollar

void times(int multiplier) {

amount= amount * 2; }

Значение параметра multiplier равно 2, так что мы подставим параметр вместо константы:

Dollar

void times(int multiplier) {

amount= amount * multiplier; }

Чтобы продемонстрировать, как хорошо мы знаем синтаксис языка Java, мы используем оператор *= (который, кстати, уменьшает дублирование):

Dollar

void times(int multiplier) {

amount *= multiplier: }

           

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

$5*2 = $10

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

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

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

Теперь мы можем пометить первый тест как завершенный. Далее мы позабо­тимся о тех странных побочных эффектах; но сначала давайте подведем итоги. Мы сделали следующее:

    создали список тестов, которые — мы знаем — нам понадобятся;

    с помощью фрагмента кода описали, какой мы хотим видеть нашу опера­цию;

    временно проигнорировали особенности среды тестирования J Unit;

    заставили тесты компилироваться, написав соответствующие заглушки;

    заставили тесты работать, используя сомнительные приемы;

    слегка улучшили работающий код, заменив константы переменными;

   добавили пункты в наш список задач, вместо того чтобы заняться всеми этими задачами сразу.

Разместил: MaxxWhite
Опубликовано: 30.04.2008
Статья "Экстремальное программирование - На примере денег - Мультивалютные деньги" прочтена 5407 раз.





Последние новости