Beck starts the chapter by clearing his todo list and starting over. He adds a todo for addition between dollars and Francs but finds that it's too tough a test so he adds a todo to implement addition between two dollars. This test proves trivial to get to pass so he does it in one big(relative to the small steps he has been taking so far) step. He comments on how TDD gives you control over the size of the steps.
Stops to consider how to even write the test for multi-currency arithmetic.
Decides on the kind of solution he wants
Creates what I think is the same things as a fake. It's an object that does the thing you want but has the same API as the one you are using.
Mentions that there is no trick to coming up with the idea of creating a fake.
Comes up with "Expression" metaphor. Arithmetic operations between involving "Expression"s result in other Expressions, "Sum" is a subtype of "Expression". Another subtype of "Expression" is "Money"
Having solved the addition of two Money
s with the same
currency he stops to consider how he can even write the test for
multi-currency addition. He decides he wants a solution that looks as
close to basic arithmetic as possible. He doesn't want to have to
consistently have to deal with different currencies and their exchange
rates when doing the arithmetic operation. This leads to the
"trick" of creating an object that behaves that he wants.
The object has to be able to do everything Money
does without
worrying about exchange rates. He comes up with the idea of the
Expression
. An Expression
is something that add
two instances ofMoney
together. An
Expression
can do arithmetic operations with other
Expressions
and return a new Expression
. This
means that:
A Money is the atomic form of an expression
This leads to the idea that the test should be assert:
assertEquals(Money.dollar(10), reduced)
Where reduced
is what you get when the result of an operation
on two Expression
s is given to the Bank
to
reduce
the value to a certain currency. The
Bank
is responsible for dealing with exchange rates.
He gives two reasons that the Bank
should be the one dealing
with exchange rates:
Expression
is at the core of what's happening and the
core should know as little as possible about what's happening
outside of it.
Expression
API as small as possible.
So he makes sure to add responsibilities that don't make sense
anywhere else. Dealing with exchange rates makes sense for a
Bank
Beck ends up with this test:
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);
}
He gets it to pass by adding the Expression
interface and
moving the definition of plus
to it, having
Money
implement Expression
, and creating a
Bank
class that implements reduce
with a
hardcoded return value.
interface Expression {
Expression plus(Money addend) {
return new Money(amount + addend.amount, currency);
}
}
class Money implements Expression {}
class Bank {
Money reduce(Expression source, String to) {
return Money.dollar(10);
}
}
This paves the way to refactor to a real implementation.
I think this quote captures the essence of this chapter:
TDD can't guarantee that we will have flashes of insight at the right moment. However, confidence-giving tests and carefully factored code give us preparation for insight, and preparation for applying that insight when it comes.
Beck has a flash of insight in this chapter that he says is at "the
heart of what we are doing". Using Expression
as the
basis for all the arithmetic between various kinds of
Money
with each individual instance of
Money
also being an Expression
seems elegant.
It's like he found the base case from which unfolds everything else
within this domain.
The other side of the quote above is that TDD cannot guarantee insight. I would take it even further that TDD can't even guarantee what Beck might call a good design because the person practicing TDD has to know what good design is and has to be aiming to go there. Several times in the this chapter and in previous chapters Beck assumes his shares his design sense.
For example when he writes:
Instead we would like a solution that lets us conveniently represent multiple exchange rates, and still allows most arithmetic-like expressions to look like, well, arithmetic.
He say "we would like", but do we? I don't know what I would like here. What does "conveniently represent multiple exchange rates" mean? How will I know if the design is "convenient"? What makes a design "convenient"? Why do want it to look like arithmetic? Why is that better than not looking like arithmetic? What's the other option?
I'm not trying to be difficult and nitpick. I think Beck assumes a certain "taste" among his readers. I think this book was written for someone with at several years of professional experience, who heard that TDD might be helpful. It assumes that the person is familiar with what Beck would consider good design and wants to know how to consistently produce good design. It's not for someone trying to figure out what good design is.
I'm starting to think that TDD by itself won't produce well designed, maintainable code. I think the person using TDD has to know where they want to go and TDD can help get them there. TDD is the driver but the programmer is the navigator.