본문 바로가기

IT/오브젝트

ch.10 상속과 코드 재사용

추상화에 의존하자.

NightlyDiscountPhone의 가장 큰 문제점은 Phone에 강하게 결합돼 있다. 즉 Phone이 변경될 경우 함께 변경될 가능성이 높다. 이 문제를 해결하는 가장 일반적인 방법은 자식 클래스가 부모클래스의 구현이 아닌 추상화에 의존하도록 만드는 것이다.

 

상속을 도입할 때 따르는 두가지 원칙

  • 두 메서드가 유사하게 보인다면 차이점을 메서드로 추출하라. 메서드 추출을 통해 두 메서드를 동일한 형태로 보이도록  만들 수 있다.
  • 부모 클래스의 코드를 하위로 내리지 말고 자식 클래스의 코드를 상위로 올려라. 부모 클래스의 구체적인 메서드를 자식 클래스로 내리는 것보다 자식 클래스의 추상적인 메서드를 부모 클래스로 올리는 것이 재사용성과 응집도 측면에서 더 뛰어난 결과를 얻을 수 있다.

차이를 메서드로 추출하라

가장 먼저 할 일은 중복 코드 안에서 차이점을 별도의 메서드로 추출하는 것이다.

 

중복 코드를 가진 Phone과 NightlyDiscountPhone 클래스에서 시작하자. Phone 클래스의 현재 모습은 다음과 같다.

public class Phone {

 public Money calculateFee() {
  Money result = Money.Zero;
  
  for(Call call : calls) {
   result = //계산
  }
  
  return result;
 }
 
}

 

NightlyDiscountPhone 클래스는 Phone과 유사하지만 calculateFee 메서드의 구현이 조금씩 다르다.

public class NightlyDiscountPhone {

 public Money calculateFee() {
  Money result = Money.Zero;
  
  for(Call call : calls) {
    if(밤이면) {
      result = // 계산
    } else {
       result = // 계산
     }
  }
  
  return result;
 }
 
}

먼저 두 클래스의 메서드에서 다른 부분을 별도의 메서드로 추출하자. 두가지는 하나의 Call에 대한 통화 요금을 계산하는 것이므로 메서드의 이름으로는 calculateCallFee로 정하고 구현해보자.

이로써 두 클래스의 calcualateFee 메서드는 완전히 동일해졌다. 이제 같은 코드를 부모 클래스로 올려보자.


중복 코드를 부모 클래스로 올려라

public abstract class AbstractPhone{
	abstract protected Money calculateCallFee(Call call);
}

해당 코드로 동통 코드를 모두 AbstractPhone으로 옮길수 있게된다.

리펙토링 후의 상속 계층

위와 같이 자식 클래스들 사이의 공통점을 부모 클래스로 옮김으로써 실제 코드를 기반으로 상속 계층을 구성할 수 있다. 즉 우리의 설계는 추상화에 의존한다고 볼수 있다.

또한 문제를 쉽게 찾을 수 있고, 쉽게 고칠수 있다고 볼수있다.


추상화가 핵심이다.

공통 코드를 이동시킨 후에 각 클래스는 서로 다른 변경의 이유를 가진다는 것에 주목하자. 

 

위에 설계에선 AbstractPhone은 전체 통화 목록을 계산하는 방법이 바뀔경우에만 변경된다.

NightlyDiscountPhone은 심야 할인 요금제의 통화 한건을 계산하는 방식이 바뀔 경우에만 변경된다.

 

부모 클래스 역시 자신의 내부에 구현된 추상 메서드를 호출하기 때문에 추상화에 의존한다고 말할수 있다. 의존성 역전 원칙에도 준수한다.

 

또한 새로운 요금제를 추가하기도 쉽다. AbstractPhone 밑에 마찬가지로 특정 요금제 폰을 추가하면 되기 때문이다.

지금까지 살펴본 모든 장점은 클래스들이 추상화에 의존하기 떄문에 얻어지는 장점이다.

추상화를 찾아내고 상속 계층 안의 클래스들이 그 추상화에 의존하도록 코드를 리펙토링하라고한다.


의도를 드러내는 이름 선택하기

추상화에 의존하는 상속 계층

위에 설명한 설계는 아쉬운점이있다. 바로 클래스의 이름이다. 심야 할인 요금제 클래스는 명확히 이름에서 구현부를 설명할 수있다. 그에 반해 Phone은 일반 요금제와 관련된 내용을 구현한다는 사실을 명확하게 전달하지 못한다.

즉 명확하게 전달하기 위에 위의 그림과 같이 클래스명을 변경한다면 조금더 적절해 보인다.


차이에 의한 프로그래밍

차이에 의한 프로그래밍 = 기존 코드와 다른 부분만을 추가함으로써 애플리케이션의 기능을 확장하는 방법

 

차이에 의한 프로그래밍의 목표는 중복 코드를 제거하고 코드를 재사용하는 것이다.

프로그래밍의 세계에서 중복 코드는 악의 근원이다. 중복코드를 제거하고 최대한 코드를 재사용하자.

상속을 이용하면 중복 코드를 제거할 수있다.

위의 그림처럼 비슷한 구현을 상속을 이용하면 오른쪽 그림과같이 나타난다고 생각한다. 

저자는 상속은 강력한 도구다. 상속을 이용하면 새로운 기능을 추가하기 위해 직접 구현해야 하는 코드의 양을 최소화할 수 있다. 

 

맹목적인 상속을 사용하는 것이 위험하다. 상속의 오용과 남용은 애플리케이션을 이해하고 확장하기 어렵게 만든다. 정말로 필요한 경우에만 상속을 사용하자.

 

상속은 코드 재사용과 관련된 대부분의 경우에 우아한 해결 방법이 아니다. 객체 지향에 능숙한 개발자들은 상속의 단점을 피하면서도 코드를 재사용할 수 있는 더좋은 방법이 있다는 사실을 알고 있다. 

바로 합성이다. 

'IT > 오브젝트' 카테고리의 다른 글

ch 12. 다형성  (0) 2021.07.24
ch11. 합성과 유연한 설계  (0) 2021.07.15
ch.09 유연한 설계  (0) 2021.07.03
DI(Dependency Injection) 와 IOC(inversion of control)  (0) 2021.04.18
[오브젝트] 5장 책임 할당하기  (0) 2020.04.01