TDD 1.0 Help

10장. 흥미로운 시간

할일 목록
  • $5 + 10CHF = $10 (환율이 2:1로 가정)

  • $5 x 2 = $10

  • amount를 private으로 만들기

  • Dollar side effect?

  • Money 반올림?

  • equals()

  • hashCode()

  • Equal null

  • Equal object

  • 5CHF x 2 = 10CHF

  • Dollar/Franc 중복

  • 공용 equals

  • 공용 times

  • Franc과 Dollar 비교하기

  • 통화?

  • testFrancMultiplication 제거

이제 times() 메서드를 제거해보자. 구현이 비슷하긴 하지만 아직 완전히 동일하지는 않다.

class Dollar(amount: Int, currency: String) : Money(amount, currency) { override fun times(multiplier: Int): Money { return dollar(amount * multiplier) } } class Franc(amount: Int, currency: String) : Money(amount, currency) { override fun times(multiplier: Int): Money { return franc(amount * multiplier) } }

어떻게 이 두 함수를 동일하게 만들까? 팩토리 메서드를 인라인시키면 어떨까?

class Dollar(amount: Int, currency: String) : Money(amount, currency) { override fun times(multiplier: Int): Money { return Dollar(amount * multiplier, currency) } } class Franc(amount: Int, currency: String) : Money(amount, currency) { override fun times(multiplier: Int): Money { return Franc(amount * multiplier, currency) } }

이제 다른점은 Dollar를 반환할 지 Franc을 반환할 지 이다. 이게 중요할까? 그냥 무지성으로 Money로 바꾸고 컴파일되는지 확인해보자. 이런 고민을 오래하는거보단 가끔은 그냥 컴퓨터에게 물어보는 것도 좋다.

Money를 일반 클래스로 바꾸라고 한다. 원하는대로 해주자

open class Money(var amount: Int, var currency: String) { open fun times(multiplier: Int): Money? { return null } }
test_fail_5123.png

위 같이 에러 메시지가 뜬다. 더 나은 메시지를 보기 위해서 toString을 정의해보자.

override fun toString(): String { return "$amount $currency" }
test_fail_94572.png

답은 맞았는데 클래스가 다르다. 문제는 equals 구현에 있다.

override fun equals(other: Any?): Boolean { return amount == (other as Money).amount && javaClass == other.javaClass }

정말로 검사해야할 건 class가 아니라 currency가 같은지 여부이다.

빨간막대 상황에서 테스트를 추가로 작성하고 싶지 않다. 하지만 현재 모델 코드를 수정하려고 하는 중이고, 테스트 없이는 모델 코드를 수정할 수 없다.

보수적인 방법으로 변경된 코드를 되돌려서 초록막대 상태로 되돌아가보자.

class Dollar(amount: Int, currency: String) : Money(amount, currency) { override fun times(multiplier: Int): Money { return Dollar(amount * multiplier, currency) } }

다시 초록막대 상태로 돌아왔다. 우리는 Dollar(10, "USD") 와 Money(10, "USD")가 같기를 바랬지만 사실 그렇지 않다고 보고된 것이다. 따라서 이것을 그대로 테스트로 사용할 수 있다.

@Test fun testDifferentClassEquality() { assertEquals(Money(10, "USD"), Dollar(10, "USD")) }
test_fail_49134.png

예상대로 실패한다. equals 코드는 클래스가 아니라 currency를 비교해야 한다.

override fun equals(other: Any?): Boolean { return amount == (other as Money).amount && currency() == other.currency() }

이제 두 하위 클래스의 times에서 Money를 반환해도 테스트가 여전히 통과한다. 따라서 times()를 상위 클래스로 끌어 올릴 수 있다.

open class Money(var amount: Int, var currency: String) { fun times(multiplier: Int): Money { return Money(amount * multiplier, currency) } }
할일 목록
  • $5 + 10CHF = $10 (환율이 2:1로 가정)

  • $5 x 2 = $10

  • amount를 private으로 만들기

  • Dollar side effect?

  • Money 반올림?

  • equals()

  • hashCode()

  • Equal null

  • Equal object

  • 5CHF x 2 = 10CHF

  • Dollar/Franc 중복

  • 공용 equals

  • 공용 times

  • Franc과 Dollar 비교하기

  • 통화?

  • testFrancMultiplication 제거

곱하기도 구현했으니 이제 아무것도 안하는 멍청한 두 하위 클래스를 제거할 수 있다.

Last modified: 31 January 2024