10장 흥미로운 시간
Money를 나타내기 위한 단 하나의 클래스만을 갖게 하기 위해
times() 구현을 동일하게 만들어야 하지만
명백한 방법이 없다.
일단 다시
Franc.times()가 Money를 반환하도록 고치고 테스트 해보자
Money 클래스를 추상클래스에서 바꾸고 times()도 바꿔서 다시 테스트.
에러 메세지다
toString() 작성하여 디버그 해보자.
expected: study.tdd.demo.Franc@10f5e2f4<10 CHF> but was: study.tdd.demo.Money@1c0c55ca<10 CHF>
문제는 equals 구현에 있다.
검사해야할 것은 클래스가 같은지가 아닌 currency가 같은지 여부이다.
보수적인 방법을 따르면 변경된 코드를 되돌려 다시 초록막대 상태로 돌아가야한다.
equals() 를 위해 테스트를 고치고 구현코드를 고쳐야 함.
이것을 테스트해보자
@Test
public void testDifferentClassEquality() {
assertTrue(new Money(10, "CHF").equals(
new Franc(10,"CHF")));
}
당연히 실패.
equals 코드가 클래스가 아니라 currency를 비교하도록 만들자.
Money
public boolean equals(Object object) {
Money money = (Money) object;
return amount == money.amount
&& currency().equals(money.currency());
}
이제 Franc.times()가 Money 를 반환하게 만들고
Dollar 에도 똑같이 적용하자
이제 구현을 상위클래스로 올릴수 있게 되었다.
- 두 times()를 일치시키기 위해 그 메서드들이 호출하는 다른 메서드를 인라인 시키고 상수를 변수로 바꿈.
- 디버깅만을 위해 toString() 을 테스트 없이 작성
- Franc 대신 Money를 반환하는 변경을 시도하고 테스트를 통해 확인
- 실험후 뒤로 물리고 다른 테스트 작성. 다른 테스트후 고치고 다시 작동시킴
11장. 모든악의 근원 ROOT OF ALL EVIL
To do: $5 + 10 CHF = $10 if CHF:USD is 2:1
Money rounding?
HashCode() Equal null Equal object
Dollar/Franc duplication
Delete testFrancMultiplication? |
이제 하위클래스 안에는 생성자 밖에 남지 않았다.
코드의 의미를 변경하지 않으면서 하위 클래스에 대한 참조를 상위 클래스에 대한 참조로 변경할 수 있다.
Money
static Money dollar(int amount) {
return new Money(amount, "USD");
}
static Money franc(int amount) {
return new Money(amount, "CHF");
}
이제 Dollar 와 Franc 을 지울수 있다.
Franc를 참조했던 testDifferentClassEquality() 테스트도 보내주자.
- 하위 클래스의 속을 들어내는 걸 완료하고, 하위클래스를 삭제했다.
- 기존에 필요했지만 새로운 구조에서 필요없게된 테스트를 제거함.
12장. 드디어, 더하기 Addition, Finally
To do: $5 + 10 CHF = $10 if CHF:USD is 2:1 $5 + $5 = $10 |
전체 더하기 전에 간단한 예제 $5 + $5 = $10 부터 시작하자
@Test
public void testSimpleAddition() {
Money sum = Money.dollar(5).plus(Money.dollar(5));
assertEquals(Money.dollar(10), sum);
}
Money
Money plus(Money addend) {
return new Money(amount + addend.amount, currency);
}
Money 와 비슷하게 동작하지만 사실 두 Money의 합을 나타내는 객체를 만드는 것이다.
한가지는 Money 의 합을 지갑과 같이 사용하는것. 여러 금액의 통화가 들어가있는 지갑.
다른 한가지는 Money 수식을 원자 형태로 나타내는것. 마치 분자식과 같이.
연산의 결과로 Expression이 생김
연산이 완료되면 환율을 이용해 결과 Expression을 단일 통화로 축약
테스트에 적용해 보자.
@Test
public void testSimpleAddition() {
Money sum = Money.dollar(5).plus(Money.dollar(5));
assertEquals(Money.dollar(10), sum);
Money reduced = bank.reduce(sum, "USD");
assertEquals(Money.dollar(10), reduced);
}
- Expression 은 우리가 하려고 하는 일의 핵심에 해당한다. 핵심이 되는 객체를 모르는척 하면 나중에 핵심객체가 유연하게 만들어질 수 있을 것이다.
- Expression과 관련이 있는 오퍼레이션이 많을거라고 추측한다. Expression에만 오퍼레이션이 추가되어 커지지 않을수 있게.
두 Money의 합은 Expression이어야 한다.
간단하게 $5 를 만들수 있다
@Test
public void testSimpleAddition() {
Money sum = Money.dollar(5).plus(Money.dollar(5));
assertEquals(Money.dollar(10), sum);
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 인터페이스 필요
Money.plus() 가 Expression 반환하도록 함
Money가 리턴값으로 Expression을 반환해야 하니 Money가 상속하도록 함 (천잰데?)
빈 Bank 클래스 필요
Bank 의 reduce() 스텁 구현
컴파일과 실패를 확인하고 가짜 구현으로 채워넣자.
@Test
public void testSimpleAddition() {
//Money sum = Money.dollar(5).plus(Money.dollar(5));
//assertEquals(Money.dollar(10), sum);
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);
}
public interface Expression {
}
class Money implements Expression {
...
Expression plus(Money addend) {
return new Money(amount + addend.amount, currency);
}
}
public class Bank {
Money reduce(Expression source, String to) {
return Money.dollar(10);
}
}
- 큰 테스트와 작은 테스트( $5 + 10CHF -> $5 + $5 )로 줄여서 발전을 나타낼수 있도록 함
- 필요한 계산 computaiton 에 대한 메타포들을 신중히 생각해 봄.
- 새 메타포에 기반하여 기존의 테스트를 재작성
- 테스트를 빠르게 컴파일
- 그리고 테스트를 실행
- 가짜 구현으로 일단 만들고 다음의 리팩토링을 기대함
'IT Book Summary > TDD' 카테고리의 다른 글
18장-20장 xUnit (0) | 2020.03.24 |
---|---|
13-17장 (0) | 2020.03.18 |
7-10장 (0) | 2020.03.09 |
4-6장 (0) | 2020.03.02 |
1-3장 (0) | 2020.02.25 |