본문으로 바로가기

10. 상속과 코드 재사용

category 기술서적오브젝트 약 1개월 전
728x90
반응형
SMALL

10. 상속과 코드 재사용

장속은 결합도를 높인다. 상속이 초래하는 부모 클래스와 자식 클래스 사이의 강한 결합이 코드를 수정하기 어렵게 만든다. 자식 클래스가 부모 클래스의 변경에 취약해지는 현상을 취약한 기반 클래스 문제라고 한다.


취약한 기반 클래스 문제

상속 관계를 추가할수록 전체 시스템의 결합도가 높아진다. 취약한 기반 클래스 문제는 캡슐화를 약화시키고 결합도를 높인다. 때문에 이것은 상속을 피해야 하는 첫 번째 이유이다.

객체를 사용하는 이유는 구현과 관련된 세부사항을 퍼블릭 인터페이스 뒤로 캡슐화할 수 있기 때문이다.

객체지향의 기반은 캡슐화를 통한 변경의 통제이다. 상속코드의 재사용을 위해 캡슐화의 장점을 희석시키고 구현에 대한 결합도를 높여 객체지향이 가진 강력함을 반감시킨다.


불필요한 인터페이스 상속문제의 예제

상속을 잘못 사용한 대표적은 사례는 Stack과 Properties가 이 있다.

Stack

Vector <--- Stack
Stack<String> stack = new Stack<>();
stack.push("1st");

stack.add(0, "2st");

assertEquals("2st", stack.pop()); // 에러

Stack은 Vector의 자식 클래스이다.

Stack은 LIFO 이지만 특정위치의 요소를 추가,삭제할 수 있다. 부모 클래스인 Vector 메서드 add를 사용해서 Stack은 자신의 규칙을 위반할 수 있게되는것이다.


Properties

HashTable <--- Properties
Properties properties = new Properties();
properties.setProperty("test", "java");
properties.setProperty("java", 17);

assertEquals(null, properties.getProperty("java")); //에러발생, 반환 값이 String이 아닐경우 null이 된다.

Properties는 Map과 유사하지만 키,값을 오로지 String 타입만 가질 수 있다.

HashTable의 인터페이스 포함되어 있는 put 메서드를 사용하면 String 타입 이외의 키와 값이라도 Properties에 저장할 수 있다.


상속은 위처럼 부모 클래스의 메서드가 자식 클래스의 내부 구조에 대한 규칙을 깨트릴 수 있다.


메서드 오버라이딩 문제

메서드 오버라이딩에서 부모의 인스턴스 혹은 메서드를 사용하기 위해서 super(..)를 사용할 경우, 이는 오동작을 일으킬수도 있는 가능성이 높다. 또한 부모 클래스와 자식 클래스의 결합도가 높아진다.


조슈아 블로치 says

클래스가 상속되길 원한다면 상속을 위해 클래스를 설계하고 내부 구현을 문서화 해야한다(메서드 오버라이딩으로 인한 파급효과, 어떤 생성자가 쓰이며, 순서 호출, 호출 결과에 대한 영향)

그러나 이는 잘된 API문서는 메서드가 무슨일을 하는지에 대해서만 기술하는것이지, 어떻게 하는지를 설명하면 안 된다는 통념을 어긴것이다.

완벽한 캡슐화를 원한다면 코드 재사용을 포기하거나 상속 대신 다른 방법을 사용해야 한다.



추상화를 통한 상속

보통 추상화+상속을 통해서 문제를 해결할 수 있다고 하는데 이것도 단점이 존재한다.

public abstract class Phone {
    private double taxRate;
    ...

    public Money calculateFee(){
        ...
        return result.plus(result.times(taxRate));
    }
}
public class RegularPhone extends Phone {
    ...

    public RegularPhone(Money amount, Duration seconds, double taxRate){
        super(taxRate);
    }
}

인스턴스 변수의 목록이 변하지 않고, 객체의 행동만 변경된다면 상관없지만, 부모 클래스의 인스턴스 변수가 변경되는 경우 자식 클래스들 또한 각각 변경되어야 한다. 메서드 구현에 대한 결합은 추상 메서드를 통해서 어느 정도 완화할 수 있지만 인스턴스 변수에 대한 잠재적인 결합을 제거할 수 있는 방법은 없다.


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

상속은 코드 재사용과 관련된 대부분의 경우에 우아한 해결 방법이 아니다. 우하한 해결 방법은 합성이다.

합성은 다음 이시간에 계속..

728x90
반응형
LIST

기술서적오브젝트카테고리의 다른글

06_메시지와 인터페이스  (0) 2025.04.17