Main Point : 조립은 상속이 야기할 수 있는 문제들을 해결한다.
- 상속과 재사용
- 상속은 상위 클래스의 기능을 재사용하고 확장하는 방법으로 사용된다.
- 상속을 통한 기능 재사용시 발생할 수 있는 단점
- 상위 클래스 변경의 어려움
- 클래스의 불필요한 증가
- 상속의 오용
- 상위 클래스 변경의 어려움
- 상위 클래스를 변경하면 그 변경의 여파가 계층도를 따라 모든 하위 클래스에 전파된다.
- 상위 클래스의 입장에서는 어떤 하위 클래스가 생성될지 모른다. 즉, 하위 클래스가 얼마나 생성될지 알 수 없기 때문에 최초에 상위 클래스를 생성할 때 완벽한 상위 클래스를 생성하는 것은 불가능하다.
- 하위 클래스가 많아질수록 상위 클래스의 사소한 변경점 하나가 큰 파장을 일으키기 때문에 상위 클래스의 변경이 더욱 어려워진다.
- 상위 클래스의 동작을 하위 클래스는 일부 알아야 기능의 재사용이 가능하다. 즉, 상위 클래스는 하위 클래스에 대해 캡슐화가 약해지며 이는 상위 클래스의 내부 구현 변경이 어려워지게 만든다.
- 클래스의 불필요한 증가
- 아래의 간단한 예시를 통해 알아보자.
Celeb이라는 클래스를 상속받은 Singer, Comedian, Youtuber 클래스가 있다고 하자. 그런데 시간이 지나면서, 가수면서 코미디언인 사람이 새로 등장할 수도 있고, 코미디언이면서 유튜버인 사람이 등장할 수도 있다.
새로운 개념의 사람들이 등장하자 새롭게 클래스를 생성해야 하는 모습이다. 이런 식으로 기능, 개념이 생길 때마다 새롭게 클래스를 생성해야 하는 것도 문제이지만, 새롭게 생긴 하위 클래스는 무엇을 상속받아야 하는지의 문제도 발생한다. 만약에 코미디언이면서 가수이면서 유튜버인 사람이 또 새롭게 등장하면, 그 클래스는 무엇을 상속받을 것인가.
- 상속 오용
- 아래의 예시를 통해 살펴보자.
public class Box extends ArrayList<Goods> {
private int maxSize;
private int currentSize;
public Box(int maxSize) {
this.maxSize = maxSize;
}
public void put(Goods goods) throws SpaceException {
if (!canPut(goods)) throw new SpaceException();
super.add(goods);
currentSize += goods.size();
}
public void extract(Goods goods) {
super.remove(goods);
currentSize -= goods.size();
}
private boolean canPut(Goods goods) {
return maxSize >= currentSize + goods.size();
}
해당 클래스는 ArrayList를 상속받아 만든 클래스이다. put과 extract 메소드는 각각 상위 클래스의 add와 remove 기능을 사용해 박스에 물건을 집어넣거나 뺀다.
문제는 이 클래스를 객체로 사용할 때 발생한다.
Goods size1Goods = new Goods(1);
Goods size1Goods = new Goods(1);
Goods size1Goods = new Goods(1);
Box box = new Box(5);
box.put(size3Goods);
box.add(size2Goods); //잘못된 사용
box.put(size1Goods); //실행 불가능해야 하지만 실행 가능함.
box 객체에 물건을 집어넣을 때, put 뿐만 아니라 add 메소드도 사용이 가능하다. 상속했기 때문이다. 문제는 add 메소드를 쓰면 Box 클래스의 currentSize 값이 변화하지 않는다. 어떤 일도 일어나지 않게 된다. 또한 box의 currentSize가 변화하지 않았기 때문에, 사이즈가 5인 상자에 사이즈가 3, 2인 물건을 넣은 뒤 다시 1을 넣겠다고 한 코드가 정상적으로 실행되게 된다.
이는 add를 put과 구분하여 사용한 사람의 문제가 아닌, add를 잘못 쓸 가능성이 높게 설계를 한 사람의 문제이다.
- 상속의 단점 해결 방법 = 조립
- 조립은 여러 객체를 묶어서 더 복잡한 기능을 제공한다
- 기능을 재사용하고 싶은 클래스가 있다면 그 클래스의 객체를 재사용 할 클래스의 필드나 필요한 시점에서 생성해서 사용한다.
- 조립을 통한 기능 재사용
- 조립 방식의 기능 재사용은 클래스 증식의 문제를 사라지게 한다.
- 특정 기능을 재사용하고 싶으면 해당 기능을 제공하는 클래스의 객체를 만들어서 사용하면 된다.
- 이 방식을 사용하면 하위 클래스도 사라지기 때문에 상위 클래스의 변경 역시 쉬워진다.
- 상속의 오용 역시 줄일 수 있다.
- 상속보다는 조립
- 기능 재사용을 위해 상속을 사용하지 않고 조립을 사용하는 것이 장점이 더 많다.
- 기능을 재사용하고 싶을 때 상속하기에 앞서 조립으로 풀 수 없는지 검토해야 한다.
- 정말 하위 타입일 경우에만 상속을 사용하자.
'Programming > 객체지향프로그래밍입문' 카테고리의 다른 글
# 의존과 DI (0) | 2023.01.29 |
---|---|
# 기능과 책임 분리 (0) | 2023.01.28 |
# 다형성과 추상화 (0) | 2023.01.19 |
# 캡슐화 (0) | 2023.01.16 |
# 객체 (0) | 2023.01.16 |
댓글