Eternity's Chit-Chat

aeternum.egloos.com



유연한 설계를 위한 패턴과 원리 - 3.리팩토링을 통한 진보 3부 Supple Design

이제 ExchangeRate 이상 필요하지 않다. 과감히 삭제하자.

 

환전에 필요한 대부분의 로직을 작성했기 때문에 exchangeUSD_KRW() 붙였던 @Ignore어노테이션을 제거하자. 이제부터는 코드를 작성한 전체 회귀 테스트를 실행하는 부분에 관해서는 언급하지 않을 것이다. 아마 지금쯤이면 어디 잘못된 부분이 없는지 찾기 위해 자신도 모르게 전체 회귀 테스트를 실행하고 있을 것이다.

 

ChangeBoothTest.java

@Test

public void exchangeUSD_KRW() {

  ChangeBooth changeBooth = new ChangeBooth();

  changeBooth.addExchangeRate(Money.USD, Money.KRW, 1027);

  assertEquals(Money.wons(1027), changeBooth.exchange(Money.dollars(1), Money.KRW));

}

 

환전을 처리하는 exchange() 메소드를 추가하자.

 

ChangeBooth.java

public Money exchange(Money amount, Currency to) {

  if (amount.isCurrencyEquals(to)) {

    return amount;

  }

 

  return Money.valueOf(exchangedAmount(amount, to), to);

}

 

private BigDecimal exchangedAmount(Money amount, Currency to) {

  return amount.getAmount().multiply(
   
BigDecimal.valueOf(getExchangeRate(amount.getCurrency(),to)));

}

 

환전 로직에서 Money times() 메소드를 사용할 필요가 없다. times() 동일한 통화를 가진 객체를 곱하는 데는 유용할 모르지만 서로 다른 통화 간의 연산에는 불필요하다. 이럴 때면 항상 YAGNI 생각한다. 지금 사용되지 않는다면 굳이 남겨둘 필요가 없다. 만약 나중에 다시 필요해지면 형상 관리 툴에서 쉽게 복구가 가능하기 때문이다. times() 메소드와 테스트 케이스 모두를 삭제하자.

 

USD에서 KRW로의 환전이 성공했으므로 역으로 KRW에서 USD로의 환전도 처리할 있어야 한다.

ChangeBoothTest.java

@Test

public void exchangeKRW_USD() {

  ChangeBooth changeBooth = new ChangeBooth();

  changeBooth.addExchangeRate(Money.KRW, Money.USD, (double)1/1027);

  assertEquals(Money.dollars(1.00), 
   
changeBooth.exchange(Money.wons(1027), Money.USD));

}

 

하지만 이것은 USD에서 KRW로의 환율과 KRW에서 USD로의 환율을 별개로 등록해야 함을 의미한다. 다음과 같이 USD에서 KRW로의 환율을 등록해도 KRW에서 USD로의 환율도 자동으로 처리할 있다면 매우 편리할 같다.

 

ChangeBoothTest.java

@Test

public void exchangeKRW_USD() {

  ChangeBooth changeBooth = new ChangeBooth();

  changeBooth.addExchangeRate(Money.USD, Money.KRW, 1027);

  assertEquals(Money.dollars(1.00),
   
changeBooth.exchange(Money.wons(1027), Money.USD));

}

 

테스트 코드를 실행시키면 ExchangeRateNotFoundException 발생한다. ChangeBooth 클래스의 addExchangeRate()에서 통화 간의 환율 변환을 동시에 등록하도록 하면 문제를 해결할 있다.

 

ChangeBooth.java

public void addExchangeRate(Currency from, Currency to, double rate) {

  rates.put(CurrencyPair.with(from, to), rate);

  rates.put(CurrencyPair.with(from, to).reverse(), 1/rate);

}

 

CurrencyPair reverse() 메소드는 from to 바꾸어서 새로운 CurrencyPair 생성한 반환한다.

 

CurrencyPair.java

public CurrencyPair reverse() {

  return CurrencyPair.with(to, from);

}

 

테스트를 실행해 보자. 빨간 막대다. exchangeKRW_USD() 테스트는 성공하지만 shouldExistUniqueExchangeRate() 테스트가 실패한다. 기존의 addExchangeRate() 메소드가 하나의 환율만을 등록했던 것에 비해 수정된 코드에서는 개의 환율을 동시에 등록하기 때문에 등록된 환율의 유일성을 체크하는 테스트가 실패한 것이다.

 

상황을 해결할 있는 가장 간단한 방법은 테스트 케이스의 이름을 shouldExistExchangeRatePair() 변경하고 환율의 개수가 2개가 되는지 검증하도록 수정하는 것이다

ChangeBoothTest.java

@Test

public void shouldExistExchangeRatePair() {

  ChangeBooth changeBooth = new ChangeBooth();

  changeBooth.addExchangeRate(Money.USD, Money.KRW, 1027);

  assertEquals(2, changeBooth.getExchangeRateSize());

 

  changeBooth.addExchangeRate(Money.USD, Money.KRW, 1035);

  assertEquals(2, changeBooth.getExchangeRateSize());

}