Last updated
Last updated
상위 클래스의 내부 구현이 달라졌을 때, 이 클래스의 API만을 이용하는 외부 클라이언트는 영향을 받지 않음
그러나 이를 상속하는 하위 클래스는, 내부 구현 변경에 의해 영향을 받을 수 있음
왜? 하위 클래스에서는 상위 클래스를 오버라이딩할 수 있고, 상위 클래스에서 내부 구현 상 내부의 메서드를 호출하는 과정에서 다른 내부 메서드를 호출하게 되면 오버라이딩된 메서드를 호출하게 됨
따라서 자기사용(self-use) 여부에 따라 동작이 달라질 수 있으며, 이는 내부 구현 방식에 해당
또한, 상위 클래스에 새로운 메서드가 추가되거나 하는 이유로 동작이 달라질 수 있음
상위 클래스의 메서드를 오버라이딩하는 과정에서 super를 호출하는 대신 아예 동작 자체를 재정의할 수 있으나, 이는 복잡하고 성능 저하를 일으킬 수 있음
아예 이름만 같고 반환 타입/매개 인자가 다른 메서드를 새로 선언할 수 있으나, 이 역시 아래의 문제점들을 야기
상위 클래스의 새 릴리스에 시그니처가 같고 반환 타입이 다른 메서드가 추가됨 - 생성한 메서드가 무시됨
상위 클래스의 새 릴리스에 시그니처, 반환 타입이 모두 같은 메서드가 추가됨 - 단순 오버라이딩처리되어 오동작 위험이 있음
하위 클래스의 재정의가 상위 클래스의 동작에도 영향을 미치게 되기 때문에 내부 구현에 영향을 받게 됨
상위 클래스와 하위 클래스는 강하게 연결되며, 상위 클래스의 새 변경점에 하위 클래스는 큰 영향을 받음
이는 하위 클래스가 사실은 상위 클래스와 is-a 관계
가 아닌데 상속하기 때문에 주로 발생하는 문제들로, 이러한 경우엔는 상속 대신 컴포지션(Composition, 합성)
을 이용해야 함
기존 클래스를 확장하는 대신, 이 클래스의 인스턴스를 내부 필드로 참조하게 하는 설계
새 클래스는 기존 클래스의 메서드를 호출하게 되고, 이를 처리를 넘긴다는 의미로 위임(delegation)
이라고 부름
또한, 새 클래스는 다른 인스턴스를 감싸는 역할을 한다는 뜻에서 래퍼(Wrapper) 클래스
라고 부름
위와 같이 컴포지션을 이용하면, Set 인터페이스를 구현한 구체 클래스를 상속하는 대신 어떤 Set의 기능이라도 유연하게 이용이 가능
MySet은 Set에 특정한 기능을 덧씌울 수 있는 클래스라는 점에서 데코레이터(Decorator) 패턴
을 이용했다고 볼 수 있음
하위 클래스가 상위 클래스와 정확히 is-a 관계
일 때에만 상속을 이용할 수 있음
리스코프 치환 원칙(서브 타입은 언제나 기반 타입으로 교체할 수 있어야 한다) 에서 또한 이 점을 강조하고 있음
이를 만족하는 방법은, 하위 클래스를 상위 클래스로 대체했을 때 모든 동작이 정상적인가? 를 보면 됨
그러나 상속을 이용하면 하위 클래스는 상위 클래스의 결함을 그대로 물려받게 되며, 상위 클래스가 확장을 고려해 설계되지 않았다면 오버라이딩한 메서드에 의해 다양한 문제가 발생할 수 있음
따라서 상속 대신 컴포지션을 사용하는 편이 더 유연한 클래스를 정의할 수 있음