Java ‐ 상속보다는 컴포지션을 사용하라[Effective Java Item 18] - dnwls16071/Backend_Summary GitHub Wiki
상속보다는 컴포지션을 사용하라
- 상속은 캡슐화를 깨뜨린다.
- 상위 클래스가 어떻게 구현되느냐에 따라 하위 클래스 동작에 이상이 생길 수 있다.
- 상위 클래스 설계자가 확장을 충분히 고려하고 문서화도 제대로 해두지 않으면 하위 클래스는 상위 클래스 변화에 맞춰 수정이 되어야만 한다.
- 또한 상위 클래스에 새로운 메서드가 추가되면 해당 클래스를 상속받는 모든 하위 클래스가 메서드를 재정의해 불필요하게 상속받아 구현해야하는 문제가 발생한다.
// Bad
public class InstrumentedHashSet<E> extends HashSet<E> {
private int addCount = 0;
public InstrumentedHashSet(int initCap, float loadFactor) {
super(initCap, loadFactor);
}
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
}
컴포지션(Composition)
- 상속은 캡슐화를 깨뜨리고 확장하기 어려운 구조를 가질 수 있게 된다.
- 컴포지션이란 기존 클래스를 확장하는 대신, 새로운 클래스를 만들고 private 필드로 기존 클래스의 인스턴스를 참조하게 하는 방법이다.
public class Engine {
public void start() {
System.out.println("엔진이 시동을 겁니다.");
}
public void stop() {
System.out.println("엔진이 멈춥니다.");
}
}
public class Wheel {
private String brand;
public Wheel(String brand) {
this.brand = brand;
}
public void roll() {
System.out.println(brand + " 바퀴가 굴러갑니다.");
}
}
// Good
public class Car {
private final Engine engine; // 클래스 인스턴스를 멤버변수로
private final Wheel[] wheels; // 클래스 인스턴스를 멤버변수로
public Car() {
this.engine = new Engine();
this.wheels = new Wheel[]{
new Wheel("미쉐린"),
new Wheel("미쉐린"),
new Wheel("미쉐린"),
new Wheel("미쉐린")
};
}
public void drive() {
System.out.println("--- 자동차 운행 시작 ---");
engine.start();
for (Wheel wheel : wheels) {
wheel.roll();
}
System.out.println("자동차를 운전합니다.");
}
public void stop() {
System.out.println("--- 자동차 운행 종료 ---");
engine.stop();
System.out.println("자동차를 멈춥니다.");
}
}