бумажник
. В
од ин бумажник можно положить несколько банкнот разных валют и
разных д о стоинств.
Еще од на метафора:
выражение
. Имеется в вид у математическое
выражение, например: (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 (это очень про сто, так как в этом интерфейсе пока что нет ни
од ной операции):
Do'stlaringiz bilan baham: |