5장에서 저지른 죄를 회개할 차례다. Dollar와 Franc은 amount 필드, 생성자, equals가 사실상 같다. 공통 상위 클래스 Money를 만들고, 중복된 코드를 한 걸음씩 위로 끌어올린다(pull up). 큰 리팩터링을 한 번에 하지 않고, 매 걸음마다 테스트를 돌려 초록을 유지하는 것이 요령이다.
작은 걸음으로 공통 부모를 뽑는다
먼저 amount 필드부터 Money로 올린다. 그다음 equals를 올린다. equals 안의 캐스팅 타입도 Dollar/Franc에서 Money로 바꿔야 위로 옮길 수 있다. 한 번에 하나씩만 옮기고, 옮길 때마다 테스트로 안전을 확인한다.
class Money { protected int amount; // private → protected, 자식이 물려받는다}class Dollar extends Money { Dollar(int amount) { this.amount = amount; } Dollar times(int multiplier) { return new Dollar(amount * multiplier); } public boolean equals(Object object) { Dollar dollar = (Dollar) object; // 아직 Dollar로 캐스팅 return amount == dollar.amount; }}
class Money { protected int amount; // private → protected, 자식이 물려받는다}class Dollar extends Money { Dollar(int amount) { this.amount = amount; } Dollar times(int multiplier) { return new Dollar(amount * multiplier); } public boolean equals(Object object) { Dollar dollar = (Dollar) object; // 아직 Dollar로 캐스팅 return amount == dollar.amount; }}
class Money { protected int amount;}class Dollar extends Money { Dollar(int amount) { this.amount = amount; } Dollar times(int multiplier) { return new Dollar(amount * multiplier); } public boolean equals(Object object) { Money money = (Money) object; // Money로 캐스팅해도 테스트는 초록 return amount == money.amount; }}
class Money { protected int amount; public boolean equals(Object object) { Money money = (Money) object; // 이제 공통 부모 한 곳에만 존재 return amount == money.amount; }}class Dollar extends Money { Dollar(int amount) { this.amount = amount; } Dollar times(int multiplier) { return new Dollar(amount * multiplier); } // equals 삭제 — 부모 것을 물려받는다. Franc의 equals도 같은 방식으로 삭제}
세 걸음 모두 테스트는 계속 초록이다. equals를 곧바로 Money로 옮기지 않고, 먼저 캐스팅 타입을 Money로 넓혀 “위로 옮겨도 안전한 상태”를 만든 다음 옮겼다. Franc의 equals도 동일한 절차로 지우면, 동치성 로직이 Money 한 곳에만 남는다.
판단 기준: 리팩터링은 테스트가 초록인 상태를 한순간도 벗어나지 않도록 잘게 쪼갠다. “캐스팅 넓히기 → 끌어올리기 → 자식에서 삭제”처럼 각 걸음이 독립적으로 안전해야 한다. 함정: equals를 Money로 올리면 Dollar와 Franc이 값만 같으면 서로 같다고 판정된다. $5 == 5CHF이 참이 되어 버린다. 명백한 버그지만, 지금은 목록에 적고 다음 장에서 테스트로 잡는다.
이 장이 남긴 할 일 목록
공통 equals- $5 == 5CHF 인가? — Dollar와 Franc이 같다고 나온다 → 7장
- times 중복 (아직 Dollar/Franc에 따로 있다)
equals(null),equals(다른 객체)처리- 10 (환율 2:1)
다음장으로 7장