디자인패턴

컴포짓(composite) 패턴

JUNGKEUNG 2022. 7. 30. 19:42
반응형

컴포짓 패턴(Composite Pattern) 이란?

 

그룹 전체와 개별 객체를 동일하게 처리할 수 있는 패턴

  • 클라이언트 입장에서는 "전체" 나 "부분"이나 모두 동일한 컴포넌트로 인식할수 있는 계층 구조를 만든다(Part-Whole Hierarchy)  
  • 컴포짓 패턴을 사용하면 클라이언트 측에서 모든 데이터를 모르더라도 복잡한 트리구조를 쉽게 다룰 수 있다.

 

 

컴포짓 패턴(Composite Pattern)은 언제 사용하는가?

  • 복합 객체와 단일 객체의 처리 방법이 다르지 않을 경우, 전체-부분 관계로 정의할 수 있다.
  • 새로운 leaf 로써의 클래스를 추가하더라도 클라이언트는 상위 추상화된 인터페이스 만을 바라보기 때문에 OCP 를 준수할 수 있다.

 

컴포짓 패턴(Composite Pattern) 장점과 단점

장점

복잡한 트리 구조를 편리하게 사용할 수 있다

다형성과 재귀를 활용할 수 있다

클라이언트 코드를 변경하지 않고 새로운 Element(요소)타입을 추가할 수 있다.

 

단점

트리를 만들어야 하기 때문에 (공통된 인터페이스를 정의해야 하기 때문에) 지나치게 일반화 해야하는 경우도 생길수 있다.

 

컴포짓 패턴 (Composite Pattern) 적용 전


import java.util.ArrayList;
import java.util.List;

public class Bag {

  private List<Item> items = new ArrayList<>();

  public void add(Item item) {
    items.add(item);
  }

  public List<Item> getItems() {
    return items;
  }
}
public class Item {

  private String name;
  private int price;

  public Item(String name, int price) {
    this.name = name;
    this.price = price;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getPrice() {
    return price;
  }

  public void setPrice(int price) {
    this.price = price;
  }
}
public class Client {

    public static void main(String[] args) {
        Item doranBlade = new Item("도란검", 450);
        Item healPotion = new Item("체력 물약", 50);

        Bag bag = new Bag();
        bag.add(doranBlade);
        bag.add(healPotion);

        Client client = new Client();
        client.printPrice(doranBlade);
        client.printPrice(bag);
    }

    private void printPrice(Item item) {
        System.out.println(item.getPrice());
    }

    private void printPrice(Bag bag) {
        int sum = bag.getItems().stream().mapToInt(Item::getPrice).sum();
        System.out.println(sum);
    }

}

위 코드에서 클라이언트 는 아래와 같은 정보를 알고 싶어한다.

1. 특정 Item 의 가격

2. Bag 속에 담겨진 Item 들의 총 가격

 

하지만, 위 클라이언트 코드를 통해 생각해 볼 점이 있다.

Item 의 가격과 Bag 속에 담겨진 Item 의 총 가격을 구하는 로직을 굳이 클라이언트가 알아야 하는가? (OCP 위배)

 

 

 

컴포짓 패턴 (Composite Pattern) 적용 후


컴포짓 패턴을 사용하여 위 문제를 해결해보자.

Component 를 만들고, Component 에는 공통적인 operation 이 들어가며 실제로 구체화 할 수 없는 인터페이스로 정의한다.

 

public interface ItemComponent {
  int getPrice();
}

 

Leaf 인 Item 을 implements 한다.

public class Item implements ItemComponent {

  private String name;
  private int price;

  public Item(String name, int price) {
    this.name = name;
    this.price = price;
  }

  @Override
  public int getPrice() {
    return this.price;
  }
}

 

 

여기서 Bag 은 Composite 에 속하는데, 클라이언트는 전체 든, 개별 이든 동일하게 사용할 수 있어야 하므로 Component 를 implements 한다.

import java.util.ArrayList;
import java.util.List;

public class Bag implements ItemComponent {

  // <Item> (leaf) 타입을 참조하면 안되고 Component 를 참조해야한다.
  private List<ItemComponent> components = new ArrayList<>(); 

  public void add(ItemComponent component) {
    components.add(component);
  }

  public List<ItemComponent> getComponents() {
    return components;
  }

  // 가격을 구하는 로직이 Bag 에게 있게된다. 클라이언트는 이 로직을 몰라도 된다.
  @Override
  public int getPrice() {
    return components.stream().mapToInt(ItemComponent::getPrice).sum();
  }
}

 

 

클라이언트는 Component 를 사용하기 때문에 Item, Bag 상관 없이 getPrice 를 사용할 수 있게된다. 

즉, 전체인지 개별인지 구분 없이 사용할 수 있게된 것이다.

 

 

 

참고


 

 

코딩으로 학습하는 GoF의 디자인 패턴 - 인프런 | 강의

디자인 패턴을 알고 있다면 스프링 뿐 아니라 여러 다양한 기술 및 프로그래밍 언어도 보다 쉽게 학습할 수 있습니다. 또한, 보다 유연하고 재사용성이 뛰어난 객체 지향 소프트웨어를 개발할

www.inflearn.com

https://hyokeun0419.tistory.com/118