합성과 유연한 설계에 대한 내용이다
자세한 내용이 궁금하면 오브젝트를 펼쳐보는 걸 추천드립니다.📖
시작하면서
합성은 구현에 의존하지 않는다는 점에서 상속과 다르다.
상속 관계는 클래스 사이의 '정적인 관계'이고
합성 관계는 객체 사이의 '동적인 관계'다.
정적인 관계는 컴파일시점으로 변경이 불가능하지만
동적인 관계는 실행시점에 동적으로 변경이 가능하다.
객체 합성이 클래스 상속보다 더 좋은 방법이다.
상속은 구현된 코드를 재사용하면 합성은 객체의 퍼블릭 인터페이스를 재사용한다.
서브클래싱(상속)에 의한 재사용을 화이트박스 재사용이라고 부른다. 상속받으면 부모 클래스의 내부가 자식 클래스에게 공개되기 때문이다.
객체 합성은 블랙박스 재사용이라 하는데, 객체의 내부는 공개되지 않고 인터페이스를 통해서 재사용되기 때문이다.
상속을 합성으로 변경하기
상속의 결합도 문제 해결
합성을 이용하면 상속의 결합도 문제를 해결할 수 있다.
class Stack<E> extends Vector {
...
}
이전 시간에 잘못된 상속의 Stack 예제다. 합성으로 변경하자.
class Stack<E> {
private Vector<E> elements = new Vector<>();
public E push(E item) {
elements.addElement(item);
return item;
}
public E pop() {
if (elements.isEmpty()) {
throw new EmptyStackException();
}
return elements.remove(elements.size() - 1);
}
}
상속으로 얻게된 불필요한 Vector의 오퍼레이션은 포함되지 않는다.
Vector가 변해도 Stack은 수정할 필요가 없게 된다.
상속으로 인한 조합의 증가
상속으로 결합도가 높아지면 수정하는데 필요한 작업이 과도하게 늘어나는 경향이 있다. 작은 기능들을 조합해 큰 기능을 만드는 경우엔 다음과 같은 문제가 생긴다
- 하나의 기능을 추가하거나 수정하기 위해 불필요한 많은 수의 클래스를 수정해야 한다.
- 단일 상속만 지원하는 언어라면 상속으로 중복 코드가 늘어난다.
핸드폰 과금 시스템의 정책이 기본 정책과 부가 정책이 있고 그 안에 세부 정책이 있다고 생각해보자.
조건들을 조합해보면 다양한 조건들이 나온다.
위의 조합을 상속으로 구현하려면 다양한 계층이 나온다.
만약에 여기서 새로운 기본 정책이 추가된다면 phone을 상속받는 새로운 계층이 그대로 하나 추가될 것이다. 하나의 기능을 추가하기 위해 많은 수의 클래스를 추가해야 할 경우를 클래스 폭발 문제 또는 조합의 폭발 문제라고 부른다.
조합의 폭발 문제 해결
시작하면서 합성은 동적인 관계라 언급했다. 합성을 사용하면 구현 시점의 정책들의 관계를 고정시킬 필요가 없고 실행 시점에 정책들의 관계를 유연하게 변경할 수 있다. 한마디로 합성은 실행 시점에 인스턴스를 조립하는 방법을 사용하는 것이다.
그럼 상속은 언제 사용하는가?
상속을 구현 상속과 인터페이스 상속으로 나눠진다는 사실을 이해해야 한다.
지금까지 언급된 상속의 문제점은 모두 구현 상속의 문제점이다.
믹스인(mixin)
믹스인은 객체를 생성할 때 코드의 일부를 클래스 안에 넣어 재사용하는 기법이다. 합성이 실행시점에 관련된 재사용방법이라면 믹스인은 컴파일시점에 필요한 코드를 조합하는 재사용 방법이다.
믹스인의 아이디어는 매우 간단하다. 객체지향 언어에서 슈퍼클래스는 서브클래스를 명시하지 않고도 정의될 수 있다. 그러나 이것은 대칭적이지는 않는다. 서브클래스가 정의될 때는 슈퍼클래스를 명시해야 한다. 믹스인(추상 서브클래스)은 결론적으로는 슈퍼클래스로부터 상속될 클래스를 명시하는 메커니즘을 표현한다. 따라서 하나의 믹스인은 매우 다양한 클래스를 도출하면서 서로 다른 서브클래스를 이용해 인스턴스화 될 수 있다.
믹스인의 이러한 특성은 다중 클래스를 위한 단일의 점진적인 확장을 정의하는 데 적절하게 만든다.
간단하게 표현하자면 믹스인을 다른 말로 다중 상속이라고 표현하기도 한다.
abstract class Run {
public abstract void run();
}
interface Move {
void move();
}
interface fly {
void fly();
}
class Mixin extends Run implements Move, fly {
@Override
public void run() {
}
@Override
public void move() {
}
@Override
public void fly() {
}
}
interface Bird extends Move, fly {
@Override
void move();
@Override
void fly();
}
'서적 > 오브젝트: 코드로 이해하는 객체지향 설계' 카테고리의 다른 글
오브젝트 13_ 서브클래싱과 서브타이핑 (0) | 2021.06.10 |
---|---|
오브젝트 12_ 다형성 (0) | 2021.06.10 |
오브젝트 10_ 상속과 코드 재사용 (0) | 2021.06.09 |
오브젝트 09_ 유연한 설계 (0) | 2021.06.09 |
오브젝트 08_ 의존성 관리하기 (0) | 2021.06.09 |