컴포지트 패턴이란?
부분-전체 계층 구조로 되어있는 트리구조를 통해 개별 객체와 복합 객체를 똑같은 방법으로 다룰 수 있다.
Why?
어떤 문제가 있길래?
- 다 알고 싶어!
- 개별 객체 (영수증)과 나머지 상자 내부에서도 어떤 것이 있는지 알고 싶고, 동일한 동작을 원할 때
위 그림을 예시로 해보겠습니다.
배달받은 상자에는 여러 세부 상자와 영수증이 들어있고, 세부 상자들은 또 다른 상자들로 이루어져 있습니다.
이럴 때 부속품들이 많을 때 각각의 객체와 공통된 상자 박스들도 동일한 결과를 얻고 싶다 가정해 보겠습니다.
상황
- 박스를 받았다!
- 버즈박스와 충전기 박스, 핸드폰, 모니터가 존재했다.
- 버즈 박스 : 크래들과 이어버드가 존재
- 충전기 박스 : 충전기 존재
- 핸드폰과 모니터 존재
여기서 각 부품들은 동일한 기능, 어떤 부품들이 들어 있는지 표시하는 것과, 총 가격이 얼마인지를 나타내는 기능이 필요하다고 생각해보겠습니다.
어떻게 해야할까요?
What?
박스와 세부 부품들을 동일한 방법으로 다루기 위해 인터페이스 부터 구현해 보겠습니다.
public interface Component {
void printInfo();
int getPrice();
}
자 끝입니다.
사실 인터페이스가 동일한 기능을 위해 사용한다고 볼 수 있는데 이러한 동작을 책임이 다른 객체에도 동일하게 한다 할 수 있을 거 같습니다.
How?
자 그럼 완성된 코드를 보여드리겠습니다.
[Box]
public class Box implements Component {
private List<Component> components = new ArrayList<>();
private Recipe recipe;
public Box() {
components.add(new PhonePackage());
components.add(new Monitor());
recipe = new Recipe();
}
@Override
public void printInfo() {
components.stream().forEach(Component::printInfo);
}
@Override
public int getPrice() {
initrecipe();
return recipe.getPrice();
}
public void initrecipe() {
this.recipe.initrecipe(components.stream()
.mapToInt(Component::getPrice)
.sum());
}
}
[PhonePackage]
public class PhonePackage implements Component {
private List<Component> components = new ArrayList<>();
public PhonePackage() {
components.add(new Buzz());
components.add(new SamsungCharger());
components.add(new SamsungPhone());
}
@Override
public void printInfo() {
components.stream().forEach(Component::printInfo);
}
@Override
public int getPrice() {
return components.stream()
.mapToInt(Component::getPrice)
.sum();
}
}
// phone
public class SamsungPhone implements Component {
@Override
public void printInfo() {System.out.println("샘송 갤럭쉬 21");}
@Override
public int getPrice() {return 100000;}
}
// charger
public class SamsungCharger implements Component {
@Override
public void printInfo() {System.out.println("샘송 충전기");}
@Override
public int getPrice() {return 5000;}
}
public class Buzz implements Component{
private List<Component> components = new ArrayList<>();
public Buzz() {
components.add(new Cradle());
components.add(new Earbud());
}
@Override
public void printInfo() {
components.stream().forEach(Component::printInfo);
}
@Override
public int getPrice() {
return components.stream()
.mapToInt(Component::getPrice)
.sum();
}
}
// Earbud
public class Earbud implements Component {
@Override
public void printInfo() {System.out.println("버즈 이어버드");}
@Override
public int getPrice() {return 10000;}
}
// cradle
public class Cradle implements Component {
@Override
public void printInfo() {System.out.println("버즈 크래들");}
@Override
public int getPrice() {return 20000;}
}
이렇게 동일한 인터페이스를 통해 구현하게 된다면 동일한 기능으로 전부 관리할 수 있습니다.
결과
public static void main(String[] args) {
Box box = new Box();
box.printInfo();
System.out.println(box.getPrice());
}
장단점
장점
- 개방 / 폐쇄 원칙을 지키고 있다. : 기존 코드를 변경하지 않고 확장이 가능
- 구조가 복잡해도 쉽게 확인 가능하다
단점
- SPR의 원칙이 깨진다. : 하나의 클래스에 두 개의 책임을 가지게 된다.
- 공통된 인터페이스를 제공하는 만큼, 기능이 유사해야 한다.
사용하는 곳
- 계층적 구조를 간단하게 접근해야 할때
'디자인패턴' 카테고리의 다른 글
[디자인 패턴 - 번외] 플라이웨이트 (0) | 2023.07.28 |
---|---|
[디자인 패턴] - 반복자 패턴 (2) | 2023.06.30 |
[디자인 패턴] 템플릿 메소드 패턴 (0) | 2023.06.28 |
[디자인 패턴] - 전략 패턴 (0) | 2023.06.16 |