ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 컴포짓 패턴 (Composite Pattern)
    디자인 패턴 2022. 7. 12. 23:05

    컴포짓 패턴 (Composite Pattern)


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

    • 클라이언트 입장에서는 ‘전체’나 ‘부분’이나 모두 동일한 컴포넌트로 인식할 수는 계층 구조 를 만든다. (Part-Whole Hierarchy)
    • 객체들의 관계를 트리 구조로 표현하며 사용자가 단일 객체와 복합 객체를 모두 동일하게 다룰 수 있도록 구조화한 패턴이다.
    • 즉, 클라이언트 입장에선 전체 부분인지, 전체의 맨 마지막 부분인지, 특정 부분인지 모르고 인터페이스를 통해 사용하게끔 한다.

     

     

     

    왜 필요한가?


    • 데이터를 다루다보면 계층형 트리 자료구조로 저장되고 이를 다루게되는 경우가 종종 생긴다.
    • 이 때 composite 패턴을 사용하면 클라이언트 측에서 모든 데이터를 모르더라도 복잡한 트리구조를 쉽게 다룰 수 있다.
    • 새로운 leaf 로써의 클래스를 추가하더라도 클라이언트는 상위 추상화된 인터페이스 만을 바라보기 때문에 OCP 를 준수할 수 있다.

     

     

     

    컴포짓 패턴 (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 를 사용할 수 있게된다. 

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

     

     

     

    장점과 단점


    장점

    • 복잡한 트리 구조를 편리하게 사용할 수 있다.
    • 다형성과 재귀를 활용할 수 있다.
    • 클라이언트 코드를 변경하지 않고, 새로운 엘리먼트 타입을 추가할 수 있다. OCP. (코드로 알아보기에서 본 printPrice)

    단점

    • 계층형 구조에서 leaf 에 해당하는 객체와 node에 해당하는 객체들 모두를 동일한 인터페이스로 다루어야하는데, 이 인터페이스 설계가 어려울 수 있다. 이럴때는 디자인패턴에 억지로 끼워맞추려는것은 아닌지 다시 생각해볼필요가 있다.

     

     

     

    참고


     

     

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

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

    www.inflearn.com

     

Designed by Tistory.