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) {

return null;

}

Теперь код компилируется и выдает нам красную полоску. Ура! У нас прогресс! Теперь можем легко подделать реализацию:

Bank

Money reduce(Expression source, String to) {

return Money.dollar(10);

}

Зеленая полоса! Теперь мы готовы выполнить рефакторинг. Но сначала подведем итоги главы. В этой главе мы

• вместо большого теста реализовали меньший тест, чтобы добиться быстрого прогресса (вместо операции $5 + 1 °CHF ограничились более простой операцией $5 + $5);

• основательно обдумали возможные метафоры для нашего предполагаемого дизайна;

• переписали первоначальный тест в свете новой метафоры;

• как можно быстрее добились компиляции теста;

• добились успешного выполнения теста;

• с трепетом посмотрели вперед, оценив объем рефакторинга, который необходим, чтобы сделать реализацию реальной.

7 В переводе на русский язык sum – это сумма. – Примеч. пер.

13. Делаем реализацию реальной

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

$5 + $5 = $10

Мы не можем вычеркнуть пункт $5 + $5, пока не удалим из кода все повторяющиеся фрагменты. Внимательно рассмотрим код. В нем нет повторяющегося кода, но есть повторяющиеся данные – $10 в «поддельной» реализации:

Bank

Money reduce(Expression source, String to) {

return Money.dollar(10);

}

Это выражение по своей сути дублирует выражение $5 + $5 в коде теста:

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);

}

Раньше, если у нас имелась «поддельная» реализация, для нас было очевидным, как можно вернуться назад и сформировать реальную реализацию. Для этого достаточно было заменить константы переменными. Однако в данном случае пока не понимаю, как вернуться назад. Поэтому, несмотря на некоторый риск, я решаю двигаться вперед:

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

$5 + $5 = $10

Операция $5 + $5 возвращает объект Money

Прежде всего, метод Money.plus() должен возвращать не просто объект Money, а реальное выражение (Expression), то есть сумму (Sum). (Возможно, в будущем мы оптимизируем специальный случай сложения двух одинаковых валют, однако это произойдет позже.)

Итак, в результате

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату