Search
Duplicate

상속과 조합

먼저 우리는 다음과 같은 이유로 상속을 사용한다.
코드를 재사용함으로써 중복을 줄이기 위해
변화에 대한 유연성 및 확장성을 증가시키기 위해
개발 시간을 단축시키기 위해
하지만 상속의 단점들은 상속을 적절하게 사용했을 때만 해당한다. 상속을 잘못 사용한다면 이는 오히려 독으로 작용한다.
상속을 적절하게 사용하지 못했을 때의 단점들을 살펴보자.

상속의 단점

캡슐화를 깨뜨린다.
상위 클래스의 구현이 하위 클래스에 노출될 수 밖에 없는 상속은 캡슐화를 무너뜨린다.
캡슐화가 무너짐으로써 하위 클래스가 상위 클래스에 강하게 결합, 의존하게 되며 이는 곧 유연한 대처가 불가능해짐을 의미한다.
즉 상속이 캡슐화에 있어서 문제를 일으키게되는 경우는 변경사항이 생겼을 때, 강하게 결합되어 있어 유연하게 대응하기 어렵기 때문이다.
public class Human { public String name; public void introduceSelf() { System.out.println("My Name is " + name); } }
Java
복사
public class Korean extends Human { public Korean(String name) { super(name); } }
Java
복사
만약 Human 클래스의 name 멤버 변수의 타입이 변경되는 경우, Human 클래스를 상속받는 클래스들에 많은 변경사항이 발생할 것이다.
또한 Human 클래스의 메소드가 변경되는 경우, Override되지 않은 하위 클래스의 해당 메소드들은 모두 변경된다.
이는 상속을 통해 강하게 결합되어있기 때문이다. 즉 상속은 하위 클래스에게 상위 클래스의 정보를 모두 알려줘버리기 때문에 문제가 발생한다.

조합이란?

조합이란 기존 클래스가 새로운 클래스의 구성요소로써 쓰이는 것이라고 생각하면 된다.
새로운 클래스를 만들고 private 필드로 기존 클래스의 인스턴스를 사용한다고 생각하면 된다.
여기서 든 생각인데 원시값을 wrapping하는 것과 비슷하다는 생각이 들었다.
또한 다양한 디자인 패턴들에서의 구현 방법이 생각났다.
Decorator에서 인터페이스를 상속받는 대신 wrapping하는 형태로 간다든지
Proxy도 마찬가지다.
위 클래스를 조합을 사용한다면 아마 다음과 같을 것이다.
public class Korean { public Human human; public Korean(String name) { this.human = Human.of(name); } public introduceSelf() { human.introduceSelf(); } }
Java
복사
책임이 완벽하게 분리되었다. 다만 바꿔끼우면 될뿐이다.
상속의 문제점에서 벗어날 수 있다.

그렇다면 언제 상속을 쓰고 조합을 쓸까?

상속을 사용하기 적합한 상황은 다음과 같다.
두 클래스 간의 관계에 대해서 확장을 고려하고 충분한 고민끝에 내린 결론이 is-a일 때
두 클래스 중 상위 클래스에서 발생할 수 있는 변경사항이 하위 클래스까지 전파되어도 괜찮을 때
상속을 사용했을 때, 코드 재사용으로 얻어지는 이점만 생각하지 말고 확장이 발생하므로 내가 작성하는 extends의 클래스에 영향을 받을수 있다는 것을 생각하자.
조합을 사용하기 적절한 상황은 다음과 같다.
두 클래스 간의 관계가 has-a일 때
즉 두 클래스 간의 관계가 동등하지 않고 어느 한쪽의 구성요소가 된다면 조합을 사용하는 것이 적절하다.