Effective Java - 상속보단 구성(Composite)
업데이트:
클린코드를 보다보면 “상속보단 Composite”이란 내용을 볼 수 있습니다.
오늘은 이 Composite(구성)이 무엇인지, 왜 Composite을 써야 하는지, 정말 상속을 대체할 수 있는지 알아보도록 하겠습니다.
상속
먼저 상속에 대해 알아보겠습니다.
이 글을 보시는 분 중에 상속을 모르시는 분은 없을테니 가볍게 보도록하겠습니다.
상속은 부모-자식 클래스로 맺어지는 관계이며 is-a 관계라고도 불립니다.
부모 클래스의 필드와 메소드를 물려받아 재사용할 수 있습니다. (물론 접근자에 따라 사용할 수 있고, 없고가 다릅니다.)
부모의 필드, 메소드를 재사용하여 중복 코드를 줄이고, 코드가 간결해지며 관계를 확실하게 계층구조를 안정적이고 단단하게 가져갈 수 있습니다.
예시
Person (부모 클래스)
public class Person {
protected String name;
protected String height;
protected String weight;
public Person(String name, String height, String weight) {
this.name = name;
this.height = height;
this.weight = weight;
}
protected void sayInfo() {
System.out.println("name = " + name);
System.out.println("height = " + height);
System.out.println("weight = " + weight);
}
}
위는 부모 클래스가 될 Person
클래스 입니다.
이름(name
), 키(height
), 몸무게(weight
)를 가지고 있으며, sayInfo
메소드를 통해 본인의 정보를 출력하고 있습니다.
Policeman (자식 클래스)
public class Policeman extends Person {
public Policeman(String name, String height, String weight) {
super(name, height, weight);
}
@Override
public void sayInfo() {
super.sayInfo();
}
}
Policeman
(경찰)이라는 클래스가 Person
을 상속하고 있습니다.
Person
의 필드와 sayInfo
메소드를 그대로 사용할 수 있음을 알 수 있습니다.
이렇게 필드와 메소드를 재활용함으로써 중복코드를 줄일 수 있게 됩니다.
하지만 보시면 @Override
를 사용함으로써 Person
클래스의 메소드를 재정의했고, 단순히 오픈된 메소드만 사용해야 한다는 캡슐화를 위반했음을 알 수 있습니다.
이게 어떤 문제가 되냐면 Policeman
객체를 Person
으로 업캐스팅을 하여 sayInfo
메소드를 호출하면 Policeman
의 sayInfo
가 호출되기 때문에 원치 않는 결과가 나올 수 있습니다.
또한, 상속은 굉장히 강한 결합이므로 위와 같은 예시에서 Person
의 필드 및 메소드가 변경된다면 Policeman
은 원하지 않음에도 영향을 받을 수 밖에 없습니다.
💡따라서 상속은 명확한 is-a 관계의 경우에만 사용해야 할 것을 권장하고 있습니다.
Composite (구성)
그럼 Composite은 무엇일까요?
상속은 is-a 관계인 반면에 Composite은 has-a 관계를 갖습니다.
이게 무슨 말이냐면 재사용하거나 활용할 객체를 내부 멤버로 갖는다고 생각하시면 됩니다.
어려운 말은 뒤로 하고 이렇게 하면 어떤 장점이 있는지 예시를 통해 알아보겠습니다.
예시
Policeman (수정된 클래스)
public class Policeman {
private Person person;
public Policeman(String name, String height, String weight) {
person = new Person(name, height, weight);
}
public void sayInfo() {
person.sayInfo();
}
}
달라진 점은 다음과 같습니다.
extends
를 제거하여 상속관계를 제거Person
을 멤버로 추가
이렇게 Composite 관계로 가져갔을때 어떤 장점이 있을까요?
먼저 상속과 달리 Person
의 오픈된 메소드만을 사용하게끔 할 수 있습니다. 즉, 캡슐화에 영향을 주지 않습니다.
또한, 상속은 부모 클래스에 변화가 일어날 경우 강제적으로 자식 클래스도 영향을 받게 되는데, 그 영향도를 줄일 수 있습니다.
❗영향이 없다는 뜻은 아닙니다. 당연히 멤버 클래스의 메소드를 사용한 경우 영향을 있을 수 있겠지만 상속관계처럼 강하게 연결되어 있지 않기 때문에 영향도를 줄일 수 있다는 것입니다.
마무리
이외에도 상속은 두개의 클래스에서 상속할 수 없다는 점 등 다양한 이유로 권장하지 않고 있습니다.
다만 “상속과 구성 둘 중 어떠한 것이 반드시 낫다”라는 명제는 없기 때문에 상황에 맞춰서 각각을 사용해야겠지만 일반적인 코드의 재사용만을 위한 상속은 지양해야 한다고 생각합니다.
코드의 재사용만을 위한다면 Composite을 사용하고, 확실한 is-a 관계이며 수정의 빈도가 적은 케이스일때는 상속을 사용해도 될 것으로 보입니다.
저 역시 보통 코드의 재사용을 위해 상속을 주로 사용했지 Composite은 잘 사용하지 않았습니다
이번 이펙티브 자바를 학습한 계기로 앞으로 자주 사용해야겠습니다.
오늘도 제 글을 봐주셔서 감사합니다. 😄
댓글남기기