상속을 통한 기능 재사용 시 다음과 같은 문제가 발생할 수 있다.
- 상위 클래스 변경이 어렵다.
- 클래스가 증가한다.
- 상속을 오용할 수 있다.
상위 클래스의 변경이 어렵다.
상속 관계에서 상위 클래스가 변경되면 상위 클래스를 상속한 하위 클래스들에 영향을 미친다.
상위 클래스 입장에서 앞으로 어떤 하위 클래스가 추가될지 알 수 없기 때문에 하위 클래스가 많아질수록 상위 클래스의 변경이 어려워진다.
또한 상위 클래스가 어떻게 동작하는지 잘 숙지하고 있어야 하위 클래스가 기능을 재사용할 수 있는데, 이는 상위 클래스는 하위 클래스에 대한 캡슐화(기능을 드러내고 구현을 감춤)가 약해진다고 할 수 있다.
클래스가 증가한다.
프로젝트 규모가 커지고 기능이 추가될 수 있다. 이런 변화에 따라 새로운 기능 추가/구현을 위해 클래스가 증가될 수 있다. 이는 프로젝트 구조를 복잡하게 만든다.
상속을 오용할 수 있다.
예를 들어 Luggage 클래스가 있는데, 자바 컬렉션 프레임워크의 ArrayList에 클래스의 타입 파라미터로 사용한다고 가정해보자.
public class Container extends ArrayList<Luggage> {
...
}
이런 상황에서 사용자가 Luggage 클래스에 정의된 add() 메소드를 사용해야 한다고 했을 때 ArrayList() 클래스의 메서드인 add()를 잘못 사용해 논리적 오류가 발생할 가능성이 높아지게 된다.
상속보단 조립
조립(Composition)
- 여러 객체를 묶어서 더 복잡한 기능을 제공.
- 보통 필드로 다른 객체를 참조하는 방식으로 조립 또는 객체를 필요한 시점에 생성하여 사용.
public class FlowController {
private Enctyptor enctyptor = new Enctyptor(); // 필드로 조립
public void process() {
...
byte[] enctyptedData = enctyptor.encrypt(data);
}
}
위 코드를 보면 암호화 기능이 필요해서 암호화 기능을 제공하는 Enctyptor 클래스를 상속받지 않고 필드나 메소드 형태로 Encryptor 객체를 생성하고 재사용하고 있다. 즉 다양한 기능을 제공하는 객체를 조립하여 더 복잡한 기능을 제공한다.
// 상속
public class Container extends ArayList<Luggage> {
private int maxSize;
private int currentSize;
...
public void put(Luggage lug) {
if(!canContain(lug)){
throw new NotEnoughSpaceException();
super.add(lug);
currentSize += lug.size();
}
}
}
// 조립
public class Container {
private int maxSize;
private int currnetSize;
// Container 클래스는 더 이상 ArrayList의 add() 메소드를 제공하지 않기 때문에
// 앞서 살펴본 '불필요한 기능을 상속하여 발생한 문제'를 방지할 수 있다.
private List<Luggage> luggages = new ArrayList();
...
public void put(Luggage lug) {
if(!canContain(lug)){
throw new NotEnoughSpaceException();
luggages.add(lug);
}
}
}
어떤 클래스의 기능을 재사용하기 위해 상속을 고려하고 있다면 우선 조립하는 방식으로 풀 수 없는지 검토해보자.
진짜 하위 타입인 경우에만 상속을 사용한다. 예를 들어 아래와 같이 Container 클래스의 경우 Container는 ArrayList의 한 종류가 아니다. 단지 '목록 관리'라는 기능을 원활히 구현하기 위해 ArrayList를 상속했을 뿐이다. 이렇게 진짜 하위 타입이 아니고 단지 기능 재사용을 위해 상속을 오용하면 문제가 발생할 수 있다.
public class Container extends ArrayList<Luggage> {
...
}
상속은 하위 타입이 상위 타입의 동작 구조에 매우 밀접하게 엮이는 경향이 있다는 것이다. 따라서 상위 타입의 구조가 변경되면 하위 타입에 영향을 줄 가능성이 높다.(하위 타입에 대한 상위 타입의 캡슐화가 약해진다)
조합은 제공하는 기능을 사용하므로 기능의 내부 구현이 변경되더라도 영향을 받을 가능성이 줄어든다.
참고
'Programming > 기초지식' 카테고리의 다른 글
Google Java Style Guide (0) | 2021.11.13 |
---|---|
[Chrome] 크롬 개발자 도구 다크 모드(Dark Mode) 변경 (0) | 2021.11.03 |
[객체 지향] 다형성과 추상화 (0) | 2021.10.22 |
[객체 지향] 캡슐화 연습 (0) | 2021.10.21 |
[객체 지향] 캡슐화(Encapsulation) (0) | 2021.10.21 |