[테스트 주도 개발] 14장_바꾸기

이번에는 2프랑을 달러로 바꾸고 싶다.

1
2
3
4
5
6
7
@Test
void testReduceMoneyDifferentCurrency(){
Bank bank = new Bank();
bank.addRate("CHF", "USD", 2);
Money result = bank.reduce(Money.franc(2), "USD");
assertEquals(Money.dollar(1), result);
}

프랑을 달러로 바꿀때 나누기 2를 하자. 다음 코드를 추가하자.

1
2
3
4
5
6
// Money
public Money reduce(String to){
int rate = (currency.equals("CHF") && to.equals("USD")) ? 2 : 1;

return new Money(amount / rate, to);
}

환율에 대한 일은 모두 Bank 가 처리해야한다. Expression.reduce() 의 인자로 Bank 를 넘겨야할 것이다. 우선 호출하는 부분을 작성하자.

1
2
3
4
5
6
class Bank {

Money reduce(Expression source, String to) {
return source.reduce(this, to);
}
}

그리구 구현 부분,

1
2
3
interface Expression {
Money reduce(Bank bank, String to);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Sum implements Expression {
Money augend;
Money addend;

Sum(Money augend, Money addend) {
this.augend = augend;
this.addend = addend;
}

public Money reduce(Bank bank, String to) {
int amount = augend.amount + addend.amount;
return new Money(amount, to);
}
}
1
2
3
4
5
6
// Money
public Money reduce(Bank bank, String to){
int rate = (currency.equals("CHF") && to.equals("USD")) ? 2 : 1;

return new Money(amount / rate, to);
}

이제 환율을 Bank 에서 계산할 수 있다.

1
2
3
4
5
6
7
8
9
10
class Bank {

Money reduce(Expression source, String to) {
return source.reduce(this, to);
}

int rate(String from, String to){
return (from.equals("CHF") && to.equals("USD")) ? 2 : 1;
}
}

그리고 환율을 Bank 에게 물어보자.

1
2
3
4
5
6
//Money
public Money reduce(Bank bank, String to){
int rate = bank.rate(currency,to);

return new Money(amount / rate, to);
}

2 가 아직도 테스트 코드 두 부분에 나온다. 이를 없애기 위해서, Bank 에서 환율표를 가지고 있다가 필요할 때 찾아볼 수 있게 하자. 두 개의 통화와 환율을 매핑하는 Hash Table 을 사용할 수 있다.
통화 쌍을 해쉬 테이블의 키로 쓰기 위해 배열을 사용할 수 있을까 ? Array.equlas() 가 각각의 원소에 대해 동치성 검사를 수행하나 ? 테스트 코드를 만들어보자.

1
2
3
4
@Test
void testArrayEquals() {
assertEquals(new Object[]{"abc"}, new Object[]{"abc"});
}

테스트가 실패한다. 키를 위한 객체를 따로 만들자.

1
2
3
4
5
6
7
8
9
class Pair {
private String from;
private String to;

public Pair(String from, String to) {
this.from = from;
this.to = to;
}
}

우린 위 클래스를 키로 쓸거니까, equals() 와 hashCode() 를 구현해야한다. 지금은 리팩토링하는 중에 코드를 작성하는 것이기 때문에, 테스트를 작성하지 않을 것이다. 이 리팩토링을마치고 모든 테스트를 통과하면, 그 코드가 실제로 사용되었다고 생각할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Pair {
private String from;
private String to;

public Pair(String from, String to) {
this.from = from;
this.to = to;
}

public boolean equals(Object object){
Pair pair = (Pair) object;
return from.equals(pair.from) && to.equals(pair.to);
}

public int hashCode() {
return 0;
}
}

0은 최악의 해쉬코드이다. 하지만, 구현이 쉽고 우리가 빨리 달리수 있다. 해시 코드를 이대로 두면 해쉬 테이블에서의 검색이 선형 검색과 비슷하게 수행될 것이다. 많은 통화를 다뤄야 한다면 실제 측정 데이터를 가지고 개선하자.
일단, 환율을 저장할 뭔가가 필요하다. 그리고, 환율을 설정할 수 있어야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Bank {
private Hashtable rates = new Hashtable();

Money reduce(Expression source, String to) {
return source.reduce(this, to);
}

int rate(String from, String to){
return (from.equals("CHF") && to.equals("USD")) ? 2 : 1;
}

public void addRate(String from, String to, int rate) {
rates.put(new Pair(from, to), new Integer(rate));
}
}

그리고, 필요할 때 환율을 얻을 수 있어야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Bank {
private Hashtable rates = new Hashtable();

Money reduce(Expression source, String to) {
return source.reduce(this, to);
}

int rate(String from, String to){
Integer rate = (Integer) rates.get(new Pair(from, to));
return rate.intValue();
}

public void addRate(String from, String to, int rate) {
rates.put(new Pair(from, to), new Integer(rate));
}
}

빨간 막대다. USD 에서 USD 로의 환율을 요청하면, 그 값이 1이 되어야한다고 기대한다는 것을 알 수 있다. 뜻 밖의 일이므로, 발견한 내용을 나중에 코드를 읽어볼 다른 사람에게도 알려주기 위해 테스트로 만들자.
이제 에러가 두개다. 다음 처럼 수정하자. 초록 막대 ~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Bank {
private Hashtable rates = new Hashtable();

Money reduce(Expression source, String to) {
return source.reduce(this, to);
}

int rate(String from, String to){
if ( from.equals(to)) return 1;
Integer rate = (Integer) rates.get(new Pair(from, to));

return rate.intValue();
}

void addRate(String from, String to, int rate) {
rates.put(new Pair(from, to), new Integer(rate));
}
}

테스트 주도 개발 <켄트 벡>

Comments