public Money calculateFee(){ Money result = Money.ZERO;
for (Call call : calls){ if(call.getFrom().getHour() >= LATE_NITGHT_HOUR){ result = result.plus( nightAmount.times(call.getDuration().getSeconds() / seconds.getSeconds()) ); }else { result = result.plus( regularAmount.times(call.getDuration().getSeconds() / seconds.getSeconds()) ); } }
return result.minus(result.times(taxRate)); } }
이처럼, 많은 코드 속에서 어떤 코드가 중복인지리 파악하는 것은 쉬운 일이 아니다. 두 클래스 사이의 중복 제거 방법으로, 클래스를 하나로 합치고 요금제를 구분하는 타입 코드를 추가하는 방법이 있다. 타입 코드 값에 따라 로직을 분기시키는 것이다. 하지만 이 방법은 낮은 응집도와 높은 결합도의 문제가 있다.
상속을 이용해 중복 코드 제거하기
1 2 3 4 5 6
publicclassNightlyDiscountPhoneextendsPhone{
@Override public Money calculateFee(){ ... }
10시 이전의 통화요금 계산하는 경우는 Phone 에 구현된 로직을 재사용하고 10시 이후의 통화 요금 계산은 NightlyDiscountPhone 에서 구현하기로 결정한다. 이 예제처럼, 상속을 이용해 코드 재사용 하기 위해서는 부모 클래스의 개발자가 세웠던 가정이나 추론 가정을 정확하게 이해해야한다. 이것은 자식 클래스의 작성자가 부모 클래스의 구현 방법에 대한 정확한 지식을 가져야한다는 것을 의미한다. 따라서 상속은 결합도를 높인다.
강하게 결합된 Phone 과 NightlyDiscountPhone
자식 클래스가 부모 클래스의 구현에 강하게 결합되면 부모 클래스의 변경에 의해 자식 클래스가 영향을 받는다.
02 취약한 기반 클래스 문제
부모 클래스의 변경에 의해 자식 클래스가 영향을 받는 현상을 취약한 기반 클래스 문제라고 한다. 취약한 기반 클래스 문제는, 자식 클래스가 부모 클래스의 구현 세부 사항에 의존하도록 만들기 때문에
“ccc” 를 키로 검색하면 null 이 반환된다. Properties 의 getProperty 메서드가 반환할 값이 String 이 아니면 null 을 반환하도록 구현되어 있기 때문이다. Hashtable 의 인터페이스에 포함되어 있는 put 메서드를 이용해서 String 타입 이외의 키 값이라도 Properties 에 저장을 한 것이다.
메서드 오버라이딩의 오작용 문제
상속은 코드 재사용을 위해 캡슐화를 희생한다.
부모 클래스와 자식 클래스의 동시 수정 문제
03 Phone 다시 살펴보기
추상화에 의존하자
부모 클래스와 자식 클래스 모두 추상화에 의존하도록 수정하자
차이를 메서드로 추출하자
변하는 것으로부터 변하지 않는 걸을 분리하자. 변하는 부분을 찾고 이를 캡슐화하자.
중복 코드를 부모 클래스로 올려라
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
publicabstractclassAbstractPhone{ private List<Call> calls = new ArrayList<>(); public Money calculateFee(){ Money result = Money.ZERO; for(Call calls : calls){ result = result.plus(calculateCallFee(call)); } return result; } abstractprotected Money calculateCallFee(Call call); }
기존 코드와 다른부분을 추가해서 애플리케이션의 기능을 확장하는 방법을 차이에 의한 프로그래밍이라고 한다. 차이에 의한 프로그래밍의 목표는 중복 코드를 제거하고 코드를 재사용 하는 것이다. 객체지향 세계에서 중복 코드 제거하고 코드 재사용하는 가장 유명한 방법은 상속이다. 상속의 오용과 남용은 애플리케이션을 이해하고 확장하기 어렵게 만든다. “정말로 필요한 경우에만 상속을 사용해라”