컴포지트(Composite) 패턴

차동준·2022년 7월 26일
0

CS-디자인패턴

목록 보기
13/16
post-thumbnail

👨‍💻 컴포지트 패턴이란?


단일객체와 복합객체를 동일하게 취급하기 위해서
객체를 트리구조로 구성하여 부분-전체 계층구조로 구현하여
단일객체와 복합객체를 똑같은 방법으로 다룰 수 있게 해주는 디자인 패턴


예시) 메뉴컴포넌트(Component)

public abstract class MenuComponent {
	// 여기서부터는 메뉴판 클래스가 사용할 메소드
	public void add(MenuComposite menuComponent) {
    	throw new UnsupportedOperationException();
    }
    
    public void remove(MenuComposite menuComponent) {
    	throw new UnsupportedOperationException();
    }
    
    // 여기서부터는 메뉴 클래스가 사용할 메소드
    public String getName() {
    	throw new UnsupportedOperationException();
    }
    
    public double getPrice() {
    	throw new UnsupportedOperationException();
    }
    
    public String getDescription() {
    	throw new UnsupportedOperationException();
    }
}

메뉴판(복합객체)와 메뉴(단일객체)를 모두 하나의 메뉴컴포넌트로 인식하여
똑같은 방법으로 처리할 수 있게 만드는 것이 컴포지트 패턴의 핵심이다.

해당 메뉴컴포넌트 추상 클래스를 상속받아서 메뉴판과 메뉴 클래스를 구현하면
두 클래스 모두 메뉴 컴포넌트로 사용할 수 있다.

이때, 각 메소드가 UnsupportedOperationException 예외를 발생시키는 이유는 복합객체와 단일객체가 필요한 메소드들이 각각 다를 수 있기 때문에
자기 역할에 맞지 않는 메소드에 대해 예외를 던지는 코드를 추상 클래스에서 미리 구현하여, 서브클래스에서 재정의(Override)하지 않을 경우 예외를 발생시키는 것이다.

예시) 메뉴판(Composite)

public class MenuComposite extends MenuComponent {
	List<MenuComponent> menus;
    
    public MenuTable() {
    	this.menus = new ArrayList<>();
    }
    
    public void add(MenuComponent menuComponent) {
    	this.menus.add(menuComponent);
    }
    
    public void remove(MenuComponent menuComponent) {
    	this.menus.remove(menuComponent);
    }
}

예시) 메뉴(Leaf)

public class Menu extends MenuComponent {
	String name;
    String description;
    double price;
    
    public Menu(String name, String description, double price) {
    	this.name = name;
        this.description = description;
        this.price = price;
    }
    
    public String getName() {
    	return this.name;
    }
    
    public String getDescription() {
    	return this.description;
    }
    
    public double getPrice() {
    	return this.price;
    }
}

메뉴판(메뉴테이블), 메뉴 클래스 모두 MenuComponent를 상속받아서
필요한 메소드만 재정의하여 구현하고 나면
아래와 같이 사용이 가능하다.

public static void main(String[] args) {
	MenuComponent menu1 = new Menu("김치찌개", "얼큰한 김치찌개", 8000.0);
    MenuComponent menu2 = new Menu("돈까스", "배터지는 돈까스", 8500.0);
    MenuComponent menu3 = new Menu("김치볶음밥", "든든한 김치볶음밥", 7000.0);
    MenuComponent menu4 = new Menu("김밥", "우영우 김밥", 3500.0);
    
    MenuComponent menus = new MenuTable();
    menus.add(menu1);
    menus.add(menu2);
    menus.add(menu3);
    menus.add(menu4);
}

컴포지트 패턴을 조금 정리하자면 아래와 같다.

메뉴(단일객체, Leaf)

메뉴판(Component, Leaf와 Composite가 구현해야 하는 추상 클래스 혹은 인터페이스로 Leaf와 Composite 모두 이 Component타입으로 인식된다.)

메뉴컴포지트(복합객체, Composite, Component 추상클래스 혹은 인터페이스 타입의 요소를 자식으로 갖는 클래스)


🔎 컴포지트 패턴의 문제점


이처럼 컴포지트 패턴은 비슷한 기능들을 갖고 있는 단일객체와 복합객체들을
하나의 구조로 인식하여 처리할 수 있게 하는 것인데

이 패턴은 어느 것이 복합객체(Composite)인지 단일객체(Leaf)인지 알 수 없는 투명성을 확보하고, 같은 방식으로 두객체 모두 처리할 수 있도록 하지만
이러한 방법은 단일 책임 원칙(SRP)을 깨는 행위이다.


🔎 컴포지트 패턴의 장단점


장점

  1. 단일객체와 복합객체가 모두 같은 타입으로 인식되기 때문에 새로운 클래스를 추가하는데 유리하다.
  2. 단일객체와 복합객체를 구분하지 않고 일관되게 프로그래밍할 수 있어서 사용자의 코드가 단순해진다.

단점

  1. SRP(단일 책임 원칙)에 위배된다.
  2. Component 추상클래스 혹은 인터페이스가 지나치게 추상화(일반화) 되어있어 복합체 구성요소에 제약을 가하는 것이 힘들다.
profile
백엔드를 사랑하는 초보 개발자

0개의 댓글