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

Итак, что же является решением в нашем случае? Предлагается создать объект, который ведет себя как объект Money, однако соответствует сумме двух объектов Money. Чтобы объяснить эту идею, я пробовал использовать несколько разных метафор. Например, можно рассматривать сумму различных денежных величин как бумажник. В один бумажник можно положить несколько банкнот разных валют и разных достоинств.

Еще одна метафора: выражение. Имеется в виду математическое выражение, например: (2 + 3) * 5. В нашем случае мы имеем дело с денежными величинами, поэтому выражение может быть таким: ($2 + 3 CHF) * 5. Класс Money – это атомарная форма выражения. В результате выполнения любых операций над денежными величинами получается объект класса Expression. Одним из таких объектов может быть объект Sum[7]. После того как операция (например, сложение нескольких значений в разных валютах) выполнена, полученный объект Expression можно привести к некоторой заданной валюте. Преобразование к некоторой валюте осуществляется на основании набора курсов обмена.

Как выразить эту метафору в виде набора тестов? Прежде всего, мы знаем, к чему мы должны прийти:

public void testSimpleAddition() {

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

}

Переменная reduced – это объект класса Expression, который создан путем применения обменных курсов в отношении объекта Expression, полученного в результате выполнения математической операции. Кто в реальном мире отвечает за применение обменных курсов? Банк. Стало быть, было бы неплохо, если бы мы могли написать

public void testSimpleAddition() {

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

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

}

(Плохо, когда в одной программе смешиваются две разные метафоры: банк и математическое выражение. Однако сейчас предлагаю не заострять на этом внимания. Сначала воплотим в жизнь то, что запланировали, а затем посмотрим, можно ли улучшить нашу систему с литературно-художественной точки зрения.)

Обратите внимание на важное дизайнерское решение: метод reduce() принадлежит объекту bank. С такой же легкостью мы могли бы написать

…educed = sum.reduce(«USD», bank).

Почему ответственным за выполнение операции reduce() сделан именно объект bank? На самом деле ответ следующий: «Это первое, что пришло мне в голову», однако такой ответ нельзя считать удовлетворительным. Почему мне в голову пришло сделать ответственным за выполнение операции reduce() именно объект класса Bank, а не объект класса Expression? Вот что мне известно на текущий момент:

• Объекты класса Expression, по всей видимости, лежат в самом сердце того, что мы делаем. Я стараюсь делать объекты, являющиеся сердцем системы, как можно менее зависимыми от всего остального мира. Благодаря этому они остаются гибкими в течение длительного времени («гибкие» в данном случае означает «простые для понимания, тестирования и повторного использования»).

• Я могу предположить, что класс Expression будет нести ответственность за множество операций. Значит, мы должны по возможности освободить этот класс от лишней ответственности и переложить часть ответственности на другие классы там, где это допустимо. В противном случае класс Expression разрастется до неконтролируемых размеров.

Конечно, это всего лишь догадки – этого не достаточно, чтобы принимать какие-либо окончательные решения, однако этого вполне достаточно, чтобы я начал двигаться в избранном направлении. Безусловно, если выяснится, что наша система вполне может обойтись без класса Bank, я переложу ответственность за выполнение метода reduce() на класс Expression. Если мы используем объект bank, значит, его необходимо создать:

public void testSimpleAddition() {

Bank bank = new Bank();

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

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

}

Сумма двух объектов Money – это объект класса Expression:

public void testSimpleAddition() {

Expression sum = five.plus(five);

Bank bank = new Bank();

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

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

}

Наконец, операция, в которой мы абсолютно уверены, – создание пяти долларов:

public void testSimpleAddition() {

Money five = Money.dollar(5);

Expression sum = five.plus(five);

Bank bank = new Bank();

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

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

}

Что надо сделать, чтобы данный код откомпилировался? Для начала создадим интерфейс Expression (мы могли бы создать класс, однако интерфейс обладает существенно меньшим весом):

Expression

interface Expression

Метод Money.plus() должен возвращать значение типа Expression:

Money

Expression plus(Money addend) {

return new Money(amount + addend.amount, currency):

}

Это означает, что класс Money должен реализовать интерфейс Expression (это очень просто, так как в этом интерфейсе пока что нет ни одной операции):

Money

class Money implements Expression

Кроме того, нам потребуется пустой класс Bank:

Bank

class Bank

Добавим в этот класс заглушку для метода reduce():

Bank

Money reduce(Expression source, String to) {

Перейти на страницу:

Все книги серии Библиотека программиста

Программист-фанатик
Программист-фанатик

В этой книге вы не найдете описания конкретных технологий, алгоритмов и языков программирования — ценность ее не в этом. Она представляет собой сборник практических советов и рекомендаций, касающихся ситуаций, с которыми порой сталкивается любой разработчик: отсутствие мотивации, выбор приоритетов, психология программирования, отношения с руководством и коллегами и многие другие. Подобные знания обычно приходят лишь в результате многолетнего опыта реальной работы. По большому счету перед вами — ярко и увлекательно написанное руководство, которое поможет быстро сделать карьеру в индустрии разработки ПО любому, кто поставил себе такую цель. Конечно, опытные программисты могут найти некоторые идеи автора достаточно очевидными, но и для таких найдутся темы, которые позволят пересмотреть устоявшиеся взгляды и выйти на новый уровень мастерства. Для тех же, кто только в самом начале своего пути как разработчика, чтение данной книги, несомненно, откроет широчайшие перспективы. Издательство выражает благодарность Шувалову А. В. и Курышеву А. И. за помощь в работе над книгой.

Чед Фаулер

Программирование, программы, базы данных / Программирование / Книги по IT

Похожие книги