지금까지 나는 상속을 단순히 재사용하는 것들이 있으면 공통적으로 묶어주기 위해 상속을 생각했다.
하지만 정말 우연한 기회로 조합이라는 것을 알게되었다.
이 영상을보다 댓글을 보니 이런 댓글이 있었다.
리팩토링 할때도 느꼈지만
상속을 사용하면 중복코드를 제거할 수 있고 확장도 쉽게 가능하고 계층구조에 유리하니 편해서 사용했던 것 같다.
하지만 위 댓글처럼 만약 상속을 통해서 엄청난 규모의 프로젝트를 했는데 최상층에 있는 부모클래스에 변동이 생기면
거기에 연관된 모든 자식클래스는 강하게 영향을 받고 결국 전부 수정을 해줘야한다.
자바스크립에서도 부모클래스에서 input 값이 stirng을 받을 것을 예상하고 로직을 만들고 상속을 통해 자식 클래스들을 여러개를 만들었는데 어느날 array로 바뀌다면 그에 따른 부모클래스도 변경을 해줘야한다 ( 예를 들어 배열의 길이가 얼마나 되고 배열안의 type은 무엇이며 빈 배열도 들어올 수 있는지 등등) 이 변경 따라 관련된 자식 클래스도 전부 수정을 해줘야한다 거기에 더해 사이드 이펙트도 전부 확인해줘야하는 문제점이 발생한다.
또한 상속은 불필요한 부모 클래스의 퍼블릭 메서드가 자식 클래스도 어쩔 수 없이 노출하게 되기에 사이드 이펙트가 발생할 수도 있다.
(특히 공개된 부모 클래스의 퍼블릭 메서드가 자식 클래스의 내부 규칙과 맞지 않을 수 있다.)
이 글을 보고 단순한 생각으로 상속을 사용하면 안되겠구나라고 느꼈고 그럽 조합은 대체 무엇인지 궁금해졌다.
조합(Composition)
조합은 이미 만들어진 객체를 이용해 조합하여, 새로운 객체를 구성하는 것이다. 즉 레고의 부품(기존에 만들어진 객체)를 조합해 붙인다고 생각하면 쉽다.
쉽게 말해 상속을 사용하지 않고 메서드를 호출하는 방식으로 동작하기 때문에 캡슐화를 손상시키지 않을 수 있다.
따라서 만약 받는 값이 string에서 array로 바뀌어도 그저 메서드 호출을 통해 값을 사용하면 되어서 호출 메서드만 수정해주면 된다.
아래 예제를 보면 쉽게 이해가 가능하다.
조합대신 상속을 사용하는 경우
아직 외계인인것을 모르는 슈퍼맨
public class Man {
public void move() {
System.out.println("걷는다");
}
public void eat() {
System.out.println("먹는다");
}
}
class SuperMan extends Man {
public void fly() {
System.out.println("날아간다.");
}
}
지금은 SuperMan이 Man을 상속하고있다.
그런데 만약 나중에 SuperMan이 Man(사람)이 아니라는 것을 알게된다면 어떻게 될까?
- 알고보니 슈퍼맨은 인간이 아니고 외계인이었다. 즉 IS-A 관계가 되지 않는다는 것을 나중에 알게되었을 경우이다.
class Man {
public boolean canDie(){
return true;
}
}
여기서 만약 슈퍼맨은 죽지 않고 불사신인데 , Man에 늙어서 죽는 기능이 생긴다면 ?
- 이걸 또 상속해서 재정의 하고 그래야 할까?
- 슈퍼맨은 필요도 없는 메서드인데 말이다.
class SuperMan extends Man{
@Override
public boolean canDie(){
return false;
}
}
조합을 사용하는 경우
public class Man {
public void move() {
System.out.println("걷는다");
}
public void eat() {
System.out.println("먹는다");
}
public boolean canDie(){
return ture;
}
}
class SuperMan {
private final Man man = new Man();
public void move() {
man.move();
}
public void eat() {
man.eat();
}
public void fly() {
System.out.println("날아간다.");
}
}
이와 같이 구성해 놓으면, Man 클래스의 변화에 대해서 의존적이지 않게된다.
- 단순히 man의 메소드를 호출하는 방식으로 바뀌기 때문이다.
결론
어느 한쪽만 사용하기보다는
어떠한 상황에서 상속이 좋은지 조합이 좋은지 잘 판단하고 사용해야한다.
아래의 조건을 만족시킨다면 상속을 사용하는 것을 고려해볼 만하다.
1. 확장을 고려하고 설계한 확실한 is - a 관계일 때
2. API에 아무런 결함이 없는 경우, 결합이 있다면 하위 클래스까지 전파돼도 괜찮은 경우
3. 기존 행동을 부분적으로 보완하는 의미의 확장으로 사용하는 경우
4. 동일하게 행동하는 인스턴스를 그룹화하는 경우
이외의 경우에는 조합을 고려해보는 게 좋다.
특히 단순 코드 재활용과 중복 코드를 제거하고 싶다면 조합을 고려하는 게 좋다.
source
https://tecoble.techcourse.co.kr/post/2020-05-18-inheritance-vs-composition/
https://yeonyeon.tistory.com/206
https://unluckyjung.github.io/oop/2021/03/15/Inheritance-Coposition/
https://www.youtube.com/watch?v=U4OSS4jJ9ns
'정보 > The 공부' 카테고리의 다른 글
캐싱 / 페이지교체 알고리즘 (2) | 2023.12.31 |
---|