1. 정의
- 객체를 트리구조로 구성해서 부분-전체 계층구조를 구현한다. 클라이언트에서 개별 객체와 복합 객체를 일관된 방법으로 다룰 수 있다.
💡 개별 객체는 리프 노드를 의미하고, 복합 객체는 일반 노드를 의미한다.
2. 예시
- 식당 메뉴를 계층구조로 만든다.
3. 그림
4. 클래스 다이어그램
5. 코드
public class Client {
public static void main(String[] args){
MenuComponent breakfast = new Menu("아침 메뉴", "08:00~11:00");
MenuComponent lunch = new Menu("점심 메뉴", "12:00~17:00");
MenuComponent dinner = new Menu("저녁 메뉴", "18:00~23:00");
MenuComponent all = new Menu("전체 메뉴", "00:00~23:59");
all.add(breakfast);
all.add(lunch);
all.add(dinner);
// 메인
breakfast.add(new MenuItem("시리얼", "요거트", 4_000));
breakfast.add(new MenuItem("식빵", "딸기쨈", 3_000));
lunch.add(new MenuItem("팬케이크", "메이플 시럽", 6_000));
dinner.add(new MenuItem("불고기", "국내산", 15_000));
dinner.add(new MenuItem("비빔밥", "국내산", 10_000));
// 디저트
MenuComponent lunchDessert = new Menu("점심 디저트", "12:00~17:00");
lunchDessert.add(new MenuItem("커피", "콜롬비아", 1_500));
lunch.add(lunchDessert);
MenuComponent dinnerDessert = new Menu("저녁 디저트", "18:00~23:00");
dinnerDessert.add(new MenuItem("포도", "", 1_000));
dinnerDessert.add(new MenuItem("딸기", "", 1_000));
dinner.add(dinnerDessert);
all.print();
}
}
/* 출력
[전체 메뉴](00:00~23:59)
[아침 메뉴](08:00~11:00)
시리얼(요거트) : 4000
식빵(딸기쨈) : 3000
[점심 메뉴](12:00~17:00)
팬케이크(메이플 시럽) : 6000
[점심 디저트](12:00~17:00)
커피(콜롬비아) : 1500
[저녁 메뉴](18:00~23:00)
불고기(국내산) : 15000
비빔밥(국내산) : 10000
[저녁 디저트](18:00~23:00)
포도() : 1000
딸기() : 1000
*/
public abstract class MenuComponent {
public void add(MenuComponent menuComponent){
throw new UnsupportedOperationException();
}
public void remove(MenuComponent menuComponent){
throw new UnsupportedOperationException();
}
public MenuComponent getChild(int i){
throw new UnsupportedOperationException();
}
public String getName(){
throw new UnsupportedOperationException();
}
public String getDescription(){
throw new UnsupportedOperationException();
}
public int getPrice(){
throw new UnsupportedOperationException();
}
public void print(){
throw new UnsupportedOperationException();
}
}
// Leaf Node
public class MenuItem extends MenuComponent{
String name, description;
int price;
public MenuItem(String name, String description, int price) {
this.name = name;
this.description = description;
this.price = price;
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return description;
}
@Override
public int getPrice() {
return price;
}
@Override
public void print() {
System.out.println(name + "("+ description +")" + " : " +price);
}
}
import java.util.ArrayList;
import java.util.List;
// Node
public class Menu extends MenuComponent{
List<MenuComponent> component = new ArrayList<>();
String name;
String description;
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
@Override
public void add(MenuComponent menuComponent) {
component.add(menuComponent);
}
@Override
public void remove(MenuComponent menuComponent) {
component.remove(menuComponent);
}
@Override
public MenuComponent getChild(int i) {
return component.get(i);
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return description;
}
@Override
public void print() {
System.out.println("["+name+"]" + "("+ description +")");
for (MenuComponent menuComponent : component) {
menuComponent.print();
}
}
}
https://github.com/kang-seongbeom/design_pattern
💡 Github에서 디자인 패턴 코드를 볼 수 있습니다.
6. 설명
- 모든 노드는 MenuComponet를 상속한 클래스로 부터 구현된다.
- 즉, 모든 노드는 MenuComponet참조변수인 로 접근할 수 있다.
- MenuItem은 리프 노드다.
- Menu는 노드이면서 복합 객체다.
- 💡 복합 객체 내부에 다른 노드가 존재할 수 있다.
- 이와 같이 계층 구조를 이루면 일관된 방법으로 노드에 접근할 수 있다.
7. 참고
- 컴포지트 패턴은 두 가지 책임이 있다.
- 계층구조 관리
- 메뉴 관련 작업처리
- 이는 단일책임원칙(SRP, Single Responsibility Principle)에 위반다.
- 대신 투명성(Transparency)을 지원한다.
- 💡 투명성 : 일관된 방법으로 접근
'디자인 패턴' 카테고리의 다른 글
디자인 패턴 - (원격)프록시 패턴 (Proxy Pattern) (0) | 2022.11.18 |
---|---|
디자인 패턴 - 상태 패턴(State Pattern) (0) | 2022.11.18 |
디자인 패턴 - 반복자 패턴(Iterator Pattern) (0) | 2022.11.18 |
디자인 패턴 - 템플릿 메소드 패턴(Template Method Pattern) (0) | 2022.11.17 |
디자인 패턴 - 퍼사드 패턴(Facade Pattern) (0) | 2022.11.17 |