1부 12장 드디어, 더하기

김희윤·2021년 4월 2일
0

테스트주도개발

목록 보기
12/16

< 현재 구현해야할 목록 >

  • $5 + 10CHF = $10 (환율이 2:1일 경우)
  • $5 * 2 = $10
  • amount를 private로 만들기
  • Dollar 부작용
  • Money 반올림
  • equals()
  • hashCode()
  • Equal null
  • Equal object
  • ~~ 5CHF * 2 = 10CHF~~
  • Dollar/Franc 중복제거
  • 공용 equals
  • 공용 times
  • Franc과 Dollar 비교하기
  • 통화?
  • testFrancMultiplication 제거
  • Dollar/Franc 제거

간단하게 $5 + $5 = $10 부터 시작하자.

@Test
    public void testSimpleAddition() {
        Money sum = Money.dollar(5).plus(Money.dollar(5));
        asserEquals(Money.dollar(10), sum);
    }
    
// Money
class Money {
    protected int amount;
    protected String currency;

    Money(int amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public String toString() {
        return amount + " " + currency;
    }

    Money times(int multiplier) {
        return new Money(amount * multiplier, currency);
    }

    String currency() {
        return currency;
    }

    public boolean equals(Object object) {
        Money money = (Money) object;
        return amount == money.amount && currency().equals(money.currency());
    }

   Money plus(Money addend) {
        return new Money(amount + addend.amount, currency);
   }

    static Money dollar(int amount) {
        return new Money(amount, "USD");
    }

    static Money franc(int amount) {
        return new Money(amount, "CHF");
    }
}

이 시스템의 어려운 제약은 다중 통화 사용에 대한 내용을 나머지 코드에서 숨기고 싶은 것이다.

  • 사용 가능한 전략은 내부값을 참조통화(복합 채권이나 환전에서 기준이 되는 화폐)로 전환하는 것이다.
    -> 하지만 이 방식으로는 환율을 쓰기가 쉽지 않다.
    -> 객체지향이 우리를 구해줄 것이다.

Money와 비슷하게 동작하지만 사실은 두 Money의 합을 나타내는 객체를 만드는 것이 정답이다!

  1. Money의 합을 지갑처럼 생각하는 것이다. 즉 한 지갑안에는 금액과 통화가 다른 여러 화폐들이 들어갈 수 있다.
  2. (2+3)*5 같은 수식으로 이용하는 것이다. 이렇게 연산을 사용하면 연산의 결과로 Expression들이 생기는데 그 중하나가 Sum이 된다.

위의 내용에 맞춰 테스트 코드를 작성해보자.

 @Test
    public void testSimpleAddition() {
        Money sum = Money.dollar(5).plus(Money.dollar(5));
        assertEquals(Money.dollar(10), sum);
        assertEquals(Money.dollar(10), reduced);
    }

reduced란 이름은 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);
    }

이 테스트 코드에서 bank가 하는 일은 하나도 없다. 그저 객체만 있으면 된다.

  @Test
    public void testSimpleAddition() {
        Money sum = Money.dollar(5).plus(Money.dollar(5));
        assertEquals(Money.dollar(10), sum);
        Bank bank = new Bank();
        Money reduced = bank.reduce(sum, "USD");
        assertEquals(Money.dollar(10), reduced);
    }

두 Money의 합은 Expression이어야 한다.

 @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 인터페이스가 필요하다 (class도 가능하지만 인터페이스가 더 가볍다.)

public interface Expression {
}

Money.plus()는 Expression을 반환해야 한다.

class Money implements Expression {
    Expression plus(Money addend) {
        return new Money(amount + addend.amount, currency);
   }
}

빈 Bank 객체 class를 만들고, reduce()를 생성한다.

public class Bank {
    Money reduce(Expression source, String to) {
        return Money.dollar(10);
    }
}

우리는

  • 큰 테스트를 작은 테스트 ($5 + 10CHF 에서 $5 + $5로 줄임)로 줄여서 발전을 볼 수 있도록 했다.
  • 우리에게 필요한 계산 메타포 들을 생각해봤다.
  • 새 메타포에 기반해서 기존의 테스트를 재작성했다.
  • 테스트를 컴파일 했다.
  • 테스트를 실행했다.
profile
블록체인, IOT, 클라우드에 관심이 많은 개발자 지망생

0개의 댓글

관련 채용 정보