관리 메뉴

나만을 위한 블로그

컴포지트 패턴(Composite pattern)이란? 본문

개인 공부/디자인 패턴

컴포지트 패턴(Composite pattern)이란?

참깨빵위에참깨빵_ 2022. 6. 12. 18:08
728x90
반응형

composite의 사전적 정의는 아래와 같다.

 

합성의, 합성물, 종합적

 

그래서 이름만 놓고 보면 어떤 것들이 합쳐지는 디자인 패턴이라고 생각된다. 그러나 위키백과에선 사전적 정의와는 다르게 설명한다.

 

https://ko.wikipedia.org/wiki/%EC%BB%B4%ED%8F%AC%EC%A7%80%ED%8A%B8_%ED%8C%A8%ED%84%B4

 

컴포지트 패턴 - 위키백과, 우리 모두의 백과사전

 

ko.wikipedia.org

컴포지트 패턴이란 객체들의 관계를 트리 구조로 구성해 부분-전체 계층을 표현하는 패턴으로 사용자가 단일 객체와 복합 객체 모두 동일하게 다루도록 한다

 

https://en.wikipedia.org/wiki/Composite_pattern

 

Composite pattern - Wikipedia

In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes a group of objects that are treated the same way as a single instance of the same type of object. The intent of a composite is to "compose" obj

en.wikipedia.org

복합 패턴은 파티셔닝 디자인 패턴이다. 복합 패턴은 같은 유형의 객체의 단일 인스턴스와 동일한 방식으로 처리되는 객체 그룹을 설명한다. 합성의 목적은 객체를 트리 구조로 "구성"해서 부분-전체 계층 구조를 나타내는 것이다. 복합 패턴을 구현하면 클라이언트가 개별 객체와 구성을 균일하게 처리할 수 있다

 

정리하면 컴포지트 패턴은 객체 간 관계를 트리 구조로 구성해서 부분-전체 계층을 표현하는 패턴이라고 한다. 트리 구조는 뭐고 부분-전체 계층은 또 뭔가? 이 2가지를 알아야 컴포지트 패턴을 더 잘 알 수 있을 것 같다.

 

https://en.wikipedia.org/wiki/Tree_(data_structure) 

 

Tree (data structure) - Wikipedia

From Wikipedia, the free encyclopedia Jump to navigation Jump to search Abstract data type simulating a hierarchical tree structure and represented as a set of linked nodes A generic, and so non-binary, unsorted, some labels duplicated, arbitrary diagram o

en.wikipedia.org

트리는 연결된 노드 집합으로 표시되는 상위 노드가 있는 하위 트리와 루트값을 써서 계층적 트리 구조를 시뮬레이션하는 널리 사용되는 추상 데이터 유형이다. 트리 자료구조는 노드 모음으로 재귀적으로 정의할 수 있다. 여기서 각 노드는 값, 노드에 대한 참조 목록으로 구성된 자료구조다. 트리의 시작은 루트 노드고 참조 노드는 자식이다. 참조가 중복되지 않으며 루트를 가리키는 것도 없다. 또는 트리를 각 노드에 할당된 값을 사용해 순서가 지정된 트리로 전체(전역적으로)로 추상적으로 정의할 수 있다. 트리는 전체적으로 수학적으로 분석될 수 있지만 실제로 자료구조로 표시될 때 일반적으로 노드별로 개별적으로 표시되고 작업된다...(중략)

 

https://www.cs.cmu.edu/~clo/www/CMU/DataStructures/Lessons/lesson4_1.htm

 

Tree Data Structure

 

www.cs.cmu.edu

...(중략) 연결 자료구조(링크드 리스트, 스택, 큐)의 개념을 노드 간에 다중 관계를 가질 수 있는 구조로 확장할 수 있다. 이런 구조를 트리라고 한다. 트리는 방향이 지정된(또는 지정되지 않은) 모서리(=선)로 연결된 노드 모음이다. 트리는 선형 자료구조인 배열, 링크드 리스트, 스택, 큐에 비해 비선형 자료구조다. 트리는 노드 없이 비어 있을 수 없고 루트라는 하나의 노드와 0개 또는 하나 이상의 하위 트리로 구성된 구조다. 트리는 다음과 같은 일반 속성이 있다

- 하나의 노드는 루트로 구별된다
- 루트를 제외한 모든 노드는 하나의 다른 노드로부터의 방향 예지로 연결된다. 방향은 부모 -> 자식이다

 

https://www.programiz.com/dsa/trees

 

Tree Data Structure

Tree Data Structure In this tutorial, you will learn about tree data structure. Also, you will learn about different types of trees and the terminologies used in tree. A tree is a nonlinear hierarchical data structure that consists of nodes connected by ed

www.programiz.com

배열, 링크드 리스트, 스택, 큐와 같은 다른 자료구조는 데이터를 순차적으로 저장하는 선형 자료구조다. 선형 자료구조에서 어떤 연산을 수행하려면 데이터 크기가 커질수록 시간 복잡도가 증가한다. 그러나 트리 자료구조는 비선형 자료구조라서 데이터에 더 빨리 쉽게 접근할 수 있다

 

트리라는 건 시작점인 하나의 원에서 여러 갈래 또는 한 갈래로 뻗쳐나가는 형태의 자료구조를 말하는 것 같다. 다음으로 부분-전체 계층은 뭘까?

 

https://stackoverflow.com/questions/15745277/what-is-the-whole-part-and-part-whole-object-relationship

 

What is the Whole-part and Part-whole object relationship?

I'm reading Head First Design Patterns book, on page 382 it says: Composite Patterns is used when you have collection of objects with Whole-Part relationships. and you want to be able to treat t...

stackoverflow.com

이 용어는 작은 것을 기반으로 더 큰 물체를 구성하는 걸 나타낸다. 컴포지트 패턴의 맥락에서 크고 작은 객체는 모두 같은 클래스의 인스턴스다. 

 

https://faq-qa.com/en/Q%26A/page=f16a99e42bcc3bd6ca5dbbec51c22b36

 

What do you mean by part whole hierarchy?

What do you mean by part whole hierarchy? A system consists of subsystems or components. Components can further be divided into smaller components. Further smaller components can be divided into smaller elements. This is a part - whole hierarchy . Everythi

faq-qa.com

시스템은 하위 시스템 또는 구성요소로 구성된다. 구성요소는 더 작은 구성요소로 더 나눌 수 있다. 더 작은 구성요소는 더 작게 나눌 수 있다. 이것이 부분-전체 계층이다. 합성(composite)의 목적은 객체를 트리 구조로 구성해서 부분-전체 계층 구조를 나타내는 것이다. 컴포지트 패턴을 구현하면 클라이언트가 개별 객체와 구성을 균일하게 처리할 수 있다

 

작은 것들을 모아서 큰 것을 구성하는 경우 부분-전체 계층이라고 하는 듯하다. 이걸 토대로 생각해보면 컴포지트 패턴은 다르게 말해서 작은 객체들을 트리 구조 형태로 엮어서 큰 프로그램을 만들 때 사용할 수 있는 패턴이라고 짐작된다.

 

컴포지트 패턴은 디자인 패턴 중 구조 패턴에 속하는데 구조 패턴의 정의는 아래와 같다.

 

https://readystory.tistory.com/131

 

[구조 패턴] 복합체 패턴(Composite Pattern) 이해 및 예제

Composite 패턴은 구조 패턴 중 하나로, 객체들의 관계를 트리 구조로 구성하여 부분-전체 계층을 표현하는 패턴입니다. 사용자는 이 복합체 패턴을 통해 단일 객체와 복합 객체 모두 동일하게 다

readystory.tistory.com

구조 패턴은 작은 클래스들을 상속, 합성을 이용해 더 큰 클래스를 생성하는 방법을 제공하는 패턴이다. 이 패턴을 사용하면 독립적으로 개발한 클래스 라이브러리를 하나처럼 사용할 수 있다. 또 여러 인터페이스를 합성해 서로 다른 인터페이스들의 동일한 추상을 제공한다...(중략)...런타임 단계에서 복합 방법이나 대상을 변경할 수 있다는 점에서 유연성을 갖는다

 

https://www.baeldung.com/java-core-structural-patterns

 

Structural Patterns in Core Java | Baeldung

Learn how some core libraries in Java have adopted the seven core structural design patterns

www.baeldung.com

구조적 디자인 패턴은 큰 객체 구조 사이의 관계를 식별해서 디자인을 단순화하는 패턴이다. 솔루션으로 반복할 수 있게 클래스, 객체를 구성하는 일반적인 방법을 설명한다.

 

정리하면 구조 패턴은 객체 간의 관계(책임)을 파악하고 단순한 디자인을 도출해내는 패턴이라고 생각된다.

 

이제 컴포지트 패턴이 어떤 건지 코드로 확인해보자. 아래 예제 코드는 밑의 사이트에서 가져왔다.

 

https://www.javatpoint.com/composite-pattern

 

Composite Pattern - Javatpoint

Composite Pattern with Patterns, design, Creational Design, Abstract Factory Pattern, singleton design patterns, Adapter, Visitor Pattern, Template Pattern, Command Pattern, State Pattern, java etc.

www.javatpoint.com

 

코드를 보기 전에 먼저 하나의 UML부터 보고 넘어간다.

 

 

이 클래스들의 역할은 아래와 같다.

 

Component
  • 컴포지션의 객체에 대한 인터페이스 선언
  • 모든 클래스에 공통적인 인터페이스의 기본 동작 구현
  • 자식 컴포넌트에 접근, 관리하기 위한 인터페이스 선언

 

Leaf
  • 컴포지션의 Leaf 객체를 나타냄
  • Leaf에는 자식이 없고 컴포지션의 객체 동작을 정의한다

 

Composite
  • 자식이 있는 컴포넌트의 동작 정의
  • 자식 컴포넌트 저장
  • 컴포넌트 인터페이스에서 자식 관련 작업 구현

 

Client
  • 컴포넌트 인터페이스를 통해 컴포지션의 객체를 조작

 

아래는 위의 설명을 바탕으로 만들 예제 코드의 UML이다.

 

 

먼저 클래스들이 수행할 기본 동작을 정의한 인터페이스를 만든다.

 

public interface Employee {
    int getId();
    String getName();
    double getSalary();
    void print();
    void add(Employee employee);
    void remove(Employee employee);
    Employee getChild(int i);
}

 

그리고 이 인터페이스를 구현하는 Accoutant, BankManager, Cashier 클래스를 만들고 인터페이스의 기능들을 각각 구현한다.

 

public class Accountant implements Employee {

    private int id;
    private String name;
    private double salary;

    public Accountant(int id, String name, double salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }

    @Override
    public int getId() {
        return id;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public double getSalary() {
        return salary;
    }

    @Override
    public void print() {
        System.out.println("=========================");
        System.out.println("Id = " + getId());
        System.out.println("Name = " + getName());
        System.out.println("Salary = " + getSalary());
        System.out.println("=========================");
    }

    @Override
    public void add(Employee employee) {
        // leaf node기 때문에 이 메서드는 구현하지 않음
    }

    @Override
    public void remove(Employee employee) {
        // leaf node기 때문에 이 메서드는 구현하지 않음
    }

    @Override
    public Employee getChild(int i) {
        // leaf node기 때문에 이 메서드는 구현하지 않음
        return null;
    }
}
import java.util.ArrayList;
import java.util.List;

// 컴포지트로 취급될 클래스
public class BankManager implements Employee {

    private int id;
    private String name;
    private double salary;
    List<Employee> employees = new ArrayList<>();

    public BankManager(int id, String name, double salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }

    @Override
    public int getId() {
        return id;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public double getSalary() {
        return salary;
    }

    @Override
    public void print() {
        System.out.println("==========================");
        System.out.println("Id = " + getId());
        System.out.println("Name = " + getName());
        System.out.println("Salary = " + getSalary());
        System.out.println("==========================");

        for (Employee employee : employees) {
            employee.print();
        }
    }

    @Override
    public void add(Employee employee) {
        employees.add(employee);
    }

    @Override
    public void remove(Employee employee) {
        employees.remove(employee);
    }

    @Override
    public Employee getChild(int i) {
        return employees.get(i);
    }
}
// leaf로 처리되는 클래스
public class Cashier implements Employee {

    private int id;
    private String name;
    private double salary;

    public Cashier(int id, String name, double salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }

    @Override
    public int getId() {
        return id;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public double getSalary() {
        return salary;
    }

    @Override
    public void print() {
        System.out.println("==========================");
        System.out.println("Id = " + getId());
        System.out.println("Name = " + getName());
        System.out.println("Salary = " + getSalary());
        System.out.println("==========================");
    }

    @Override
    public void add(Employee employee) {
        // leaf node기 때문에 이 메서드는 구현하지 않음
    }

    @Override
    public void remove(Employee employee) {
        // leaf node기 때문에 이 메서드는 구현하지 않음
    }

    @Override
    public Employee getChild(int i) {
        // leaf node기 때문에 이 메서드는 구현하지 않음
        return null;
    }
}

 

이제 메인에서 각 클래스의 생성자를 호출해 원하는 값을 넣어 인터페이스를 통해 객체를 조작한다.

 

public class Main {
    public static void main(String[] args) {
        Employee emp1 = new Cashier(101, "Sohan Kumar", 20000.0);
        Employee emp2 = new Cashier(102, "Mohan Kumar", 25000.0);
        Employee emp3 = new Accountant(103, "Seema Mahiwal", 30000.0);

        Employee manager1 = new BankManager(100, "Ashwani Rajput", 100000.0);

        manager1.add(emp1);
        manager1.add(emp2);
        manager1.add(emp3);
        manager1.print();
    }
}

 

이 코드를 실행하면 아래와 같은 화면이 나온다.

 

==========================
Id = 100
Name = Ashwani Rajput
Salary = 100000.0
==========================
==========================
Id = 101
Name = Sohan Kumar
Salary = 20000.0
==========================
==========================
Id = 102
Name = Mohan Kumar
Salary = 25000.0
==========================
=========================
Id = 103
Name = Seema Mahiwal
Salary = 30000.0
=========================

 

이런 컴포지트 패턴은 언제 사용하면 좋고 언제 사용하지 말아야 할까?

 

https://www.geeksforgeeks.org/composite-design-pattern/

 

Composite Design Pattern - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

컴포지트 패턴은 클라이언트가 객체의 구성, 개별 객체 간의 차이를 무시해야 할 때 써야 한다. 개발자가 같은 방식으로 여러 객체를 쓰고 있으며 각각을 처리하기 위해 거의 동일한 코드를 쓰는 경우가 많다면 컴포지트 패턴이 좋은 선택이며 이 상황에선 기본(primitives) 및 복합을 같은 종류로 취급하는 게 덜 복잡하다

1. 객체 수가 적으면 메모리 사용량이 줄고 OutOfMemoryError 같은 메모리 관련 오류가 발생하지 않게 관리한다
2. 자바에서 객체를 생성하는 것은 빠르지만 객체를 공유함으로써 프로그램 실행 시간을 줄일 수 있다

- 언제 컴포지트 패턴을 쓰지 말아야 하는가?

1. 컴포지트 패턴을 쓰면 복합 컴포넌트의 유형을 제한하기가 어려워진다. 따라서 객체의 전제 또는 부분 계층을 나타내지 않으려는 경우엔 쓰지 말아야 한다
2. 컴포지트 패턴은 디자인을 지나치게 일반적으로 만들 수 있다. 컴포지트의 컴포넌트를 제한하기가 더 어렵다. 때로는 함성물에 특정 컴포넌트만 포함되기를 원할 수 있다. 컴포지트 패턴을 쓰면 이런 제약 조건을 적용하기 위해 타입 시스템에 의존할 수 없다. 대신 런타임 검사를 써야 한다
반응형

'개인 공부 > 디자인 패턴' 카테고리의 다른 글

Repository 패턴이란?  (0) 2023.07.17
프록시 패턴(Proxy pattern)이란?  (0) 2023.03.05
데코레이터(Decorator) 패턴이란?  (0) 2022.01.16
빌더(Builder) 패턴이란?  (0) 2022.01.02
팩토리 패턴이란?  (0) 2021.10.05
Comments