[테스트 주도 개발] 8장_객체 만들기

times() 의 구현 코드가 거의 같다.

1
2
3
4
5
6
7
8
9
// Franc Class
Franc times(int multiplier) {
return new Franc(amount * multiplier);
}

// Dollar Class
Dollar times(int multiplier) {
return new Dollar(amount * multiplier);
}

양쪽 모두 Money 를 반환하게 만들면 더 비슷해진다.

1
2
3
4
5
6
7
8
9
// Franc Class
Money times(int multiplier) {
return new Franc(amount * multiplier);
}

// Dollar Class
Money times(int multiplier) {
return new Dollar(amount * multiplier);
}

이제, Money 의 두 하위 클래스는 많은 일을 하지 않으므로 제거하고 싶다. 한번에 이렇게 큰 단계를 밟는 것은 TDD 를 효과적으로 보여주기 적절치 않을 것 같다.
하위 클래스에 대한 직접적인 참조가 적어지면 하위 클래스를 제거하기 쉬울 것 같다.

1
2
3
4
5
6
7
@Test
void testMultiplication() {
Dollar five = Money.dollar(5);
assertEquals(new Dollar(10), five.times(2));
assertEquals(new Dollar(15), five.times(3));
}

구현 코드는 Dollar 를 생성해 반환한다.

1
2
3
4
// Money Class
static Dollar dollar(int amount) {
return new Dollar(amount);
}

Test 선언부도 다음과 같이 변경한다.

1
2
3
4
5
6
7
@Test
void testMultiplication() {
Money five = Money.dollar(5);
assertEquals(new Dollar(10), five.times(2));
assertEquals(new Dollar(15), five.times(3));
}

컴파일러가 이제 times() 가 정의되어 있지 않았다는 사실을 알려준다. 지금은 이걸 구현할 준비가 안되어 있기 때문에, Money 를 추상 클래스로 변경한 후 Money.times() 를 선언하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
abstract class Money {
protected int amount;

static Dollar dollar(int amount) {
return new Dollar(amount);
}

abstract Money times(int multiplier);

public boolean equals(Object object) {
Money money = (Money) object;

return amount == money.amount && getClass().equals(money.getClass());
}
}

이제 팩토리 메서드의 선언을 바꿀 수 있다.

1
2
3
4
static Money dollar(int amount) {
return new Dollar(amount);
}

모든 테스트가 실행된다. 이제 팩토리 메서드를 테스트 코드의 나머지 모든 곳에 사용하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
void testMultiplication() {
Money five = Money.dollar(5);
assertEquals(Money.dollar(10), five.times(2));
assertEquals(Money.dollar(15), five.times(3));
}

@Test
void testEquality() {
assertTrue(Money.dollar(5).equals(Money.dollar(5)));
assertFalse(Money.dollar(5).equals(Money.dollar(6)));
assertTrue(new Franc(5).equals(new Franc(5)));
assertFalse(new Franc(5).equals(new Franc(6)));

assertFalse(new Franc(5).equals(Money.dollar(5)));
}

어떤 클라이언트 코드도 Dollar 라는 이름의 하위 클래스가 있다는 사실을 모른다. 하위 클래스의 존재를 테스트에서 분리함으로써 어떤 모델 코드에도 영향을 주지 않고 상속 구조를 마음대로 변경할 수 있게 됐다.
이제 Franc 에도 적용하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
void testFrancMultiplication() {
Franc five = Money.franc(5);
assertEquals(Money.franc(10), five.times(2));
assertEquals(Money.franc(15), five.times(3));
}

@Test
void testEquality() {
assertTrue(Money.dollar(5).equals(Money.dollar(5)));
assertFalse(Money.dollar(5).equals(Money.dollar(6)));
assertTrue(Money.franc(5).equals(Money.franc(5)));
assertFalse(Money.franc(5).equals(Money.franc(6)));

assertFalse(Money.franc(5).equals(Money.dollar(5)));
}

구현은 Money.dollar() 와 유사하다.

1
2
3
4
// Money Class
public static Franc franc(int amount) {
return new Franc(amount);
}

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

Comments