$5 + 10CHF이라는 최종 목표에 다가서려면 프랑(Franc)이라는 두 번째 통화가 필요하다. 가장 빠른 길은 부끄럽게도 Dollar를 통째로 복사해 Franc으로 이름만 바꾸는 것이다. 벡은 이 중복을 알면서도 저지른다. “죄악을 알고 저지르기(sinning)“의 핵심은 죄를 짓는 게 아니라, 초록을 먼저 확보한 뒤 반드시 회개(중복 제거)하는 데 있다.

복사-붙여넣기로 빠르게 초록에 도달한다

지금 목표는 우아함이 아니라 속도다. Franc 테스트를 통과시킬 수만 있으면 된다. 중복은 다음 장에서 제거할 것이므로, 지금은 Dollar와 똑같은 구조의 Franc을 만들어 초록 막대부터 켠다. 중복이 눈앞에 드러나야 어디를 지울지도 보인다.

Refactoring Step 빨강 — Franc 테스트를 추가한다
1/2
@Testpublic void testFrancMultiplication() {    Franc five = new Franc(5);    assertEquals(new Franc(10), five.times(2));    assertEquals(new Franc(15), five.times(3));}// Franc 클래스가 없다 → 컴파일 실패

테스트는 초록이 된다. 그리고 이제 Dollar와 Franc이 나란히 놓여 중복이 노골적으로 드러난다. amount 필드, 생성자, times, equals — 이름만 다르고 몸통은 같다. 이 눈에 보이는 중복이 다음 장에서 상위 클래스를 뽑아낼 근거가 된다.

판단 기준: 완벽한 추상화를 미리 설계하지 말고, 일단 복제해서 초록을 만든 뒤 드러난 중복을 보고 추상화한다. 두 사례가 실제로 있어야 공통점이 무엇인지 정확히 안다. 함정: 복사-붙여넣기 자체는 죄가 아니다. 복사해 놓고 회개하지 않는 것, 즉 중복을 그대로 방치하는 것이 진짜 죄다. 반드시 다음 걸음에서 지운다.

이 장이 남긴 할 일 목록

  • Franc과 Dollar의 중복 — 상위 클래스로 묶기 → 6장
  • 공통 equals
  • times 중복
  • equals(null), equals(다른 객체) 처리
  • hashCode()
  • 10 (환율 2:1)

다음장으로 6장