Eternity's Chit-Chat

aeternum.egloos.com



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

잠깐! 잠깐! 잠시 호흡을 가다듬고 생각을 해보자. 도메인 전문가들은 USD에서 KRW로의 환율을 등록할 2개의 환율을 등록한다고 생각할까? 그렇지 않다. 도메인 전문가들은 USD에서 KRW로의 환율과 KRW에서 USD로의 환율은 동일하다고 생각한다. , 내부적으로는 하나의 환율만을 유지해야하고 필요하다면 원래 환율의 역수를 취해 환전을 처리해야 한다. 다시 한번 도메인의 의미를 코드에 반영하기 위해 노력해야 때가 같다.

 

KRW에서 USD로의 환율을 추가하면 암묵적으로 USD에서 KRW로의 환율도 동시에 추가되어야 한다. 그러나 실제로 KRW에서 USD로의 환율과 USD에서 KRW로의 환율을 개별적으로 관리하는 것이 아니라 하나의 환율 정보를 사용해서 양방향 변환 모두에 사용해야 한다. 기존에 작성된 테스트 케이스를 회귀 테스트로 사용할 있기 때문에 안심하고 리팩토링에 전념할 있다.

 

개의 환율 대신 하나의 환율만을 추가하도록 addExchangeRate() 수정한다. KRW USD로의 환율을 추가하기 위해서는 Map 이미 KRW에서 USD로의 환율이나 USD에서 KRW로의 환율 중의 하나가 키로 존재한다면 해당 환율을 먼저 제거한 환율을 추가해야 한다.

 

ChangeBooth.java

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

  removeCurrencyPair(CurrencyPair.with(from, to));

  addCurrentPair(CurrencyPair.with(from, to), rate);

}

 

private void addCurrentPair(CurrencyPair pair, double rate) {

  rates.put(pair, rate);

}

 

private void removeCurrencyPair(CurrencyPair pair) {

  if (rates.remove(pair) == null) {

    rates.remove(pair.reverse());

  }

}

 

환율을 반환하는 getExchangeRate()에서는 통화를 사용해서 CurrencyPair 구성한 해당 환율을 검색한다. 만약 환율이 존재하지 않으면 CurrencyPair 역변환해서 환율을 구하고  경우에도 환율이 존재하지 않는다면 ExchangeRateNotFoundException 던진다.

 

ChangeBooth.java

double getExchangeRate(Currency from, Currency to)

  throws ExchangeRateNotFoundException {

  if (rates.containsKey(CurrencyPair.with(from, to))) {

    return rates.get(CurrencyPair.with(from, to));

  }

 

  if (rates.containsKey(CurrencyPair.with(from, to).reverse())) {

    return 1 / rates.get(CurrencyPair.with(from, to).reverse());

  }

 

  throw new ExchangeRateNotFoundException();

}

 

회귀 테스트를 실행시켜 모든 것이 안전하다는 것을 확인하자. 완성된 테스트 케이스는 ChangeBooth 포함된 부수 효과에 대한 ASSERTION 표현하고 있다.

 

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));

}

 

@Test

public void shouldExistUniqueExchangeRate() {

  ChangeBooth changeBooth = new ChangeBooth();

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

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

  assertEquals(1, changeBooth.getExchangeRateSize());

}