Design Pattern, Composite

2022. 2. 1. 13:15ETC/Design Patterns

반응형

Object Structural Pattern

Composite Pattern

 

-----------------    INDEX     -----------------

 

Composite Pattern ?

Recursive

Structure

Sample Code: Java

Pros & Cons

관련 패턴

 

----------------------------------------------

 

 

Compose objects into tree structures to represent part-whole hierarchies.
Composite lets clients treat individual objects and compositions of objects uniformly.


- GoF Design Patterns

 

 

복합체 패턴은 객체들의 관계를 트리 구조로 구성하여 부분-전체 계층을 표현하는 패턴으로, 사용자가 단일 객체와 복합 객체 모두 동일하게 다루도록 합니다.

 

위 문장을 읽으면 잘 이해가 안갈수도 있는데요.

파일시스템의 구조를 생각하면 쉽게 이해할 수 있습니다.

 

파일 시스템은 폴더 안에 폴더가 있을 수도, 혹은 파일들이 있을 수도 있습니다.

폴더 안에 폴더를 넣고,  그 안에 폴더를 넣고 파일을 넣은 트리 구조를 생각해보면 재귀적인 형식이라는 것이 느껴지나요?

이렇게 폴더라는 객체를 트리 구조로 구성하여 부분-전체 계층으로 표현하는 계층이 바로 복합체 패턴입니다.

 

 

 

복합체 패턴의 특징에는 아래의 Partitioning Pattern, Cache, Order, Recursive 가 있습니다.

 

 

Partitioning Pattern

복합체 패턴은 *분할 디자인 패턴partitioning design pattern의 하나입니다.

복합체 패턴을 이용하면 객체의 상위, 하위 체계를 파악할 수 있고 일대일, 다대일을 처리하는 데도 매우 유용합니다.

 

* 분할 패턴; 데이터 저장소를 수평 파티션 또는 분할 집합으로 나눕니다. 이를 통해 대량의 데이터를 저장하고 액세스할 때 확장성을 개선할 수 있습니다.

 

 

Cache

복합체 패턴에서 관리되는 객체와 Leaf 구조의 크기는 예상할 수 없으며, 기하급수적으로 커질 수 있습니다.

관리되는 복합 객체가 너무 커지면 구조를 분석하고 처리하는 데 많은 자원이 할당되므로, 이를 빠르게 처리하기 위해 별도의 캐시 처리 동작을 만들어 둘 수 있습니다.

 

 

Order

선택적으로 여러 객체를 하위 객체로 포함할 경우 복합적인 구조가 특별한 순서로 관리될 수 있습니다.

이를 위해서는 저장되는 하위 객체의 순서를 관리하는 별도의 로직을 추가합니다.

 

 

Recursive

또, 재귀적인 특성 상 하나의 객체를 호출하면 서브로 갖고 있는 자식의 객체 메서드를 호출할 수 있습니다.

 

 

 

 

Recursive

 

복합체 패턴은 재귀적으로 구성됩니다.

 

복합체 패턴을 생성하고 나면 아래의 그림과 같이 구성됩니다. 

Composite 객체가 Composite 객체를 담고, 자식을 담지 않는 객체는 모두 Leaf라고 부릅니다.

 

출처: GoF Design Patterns

 

GoF 책의 예시로 재귀적인 특징을 확인해봅시다.

아래의 그림을 보면, Picture가 모든 객체를 포함할 수 있고, 그 아래 Line, Rectangle, Text를 담을 수 있는 것을 확인할 수 있습니다.

출처: GoF Design Patterns

 

이런 구조를 제작하기 위해선 어떤 클래스들이 필요할까요?

UML로 표현하면 아래와 같이 표현할 수 있습니다.

출처: GoF Design Patterns

 

먼저, Graphic(Component)이라는 인터페이스를 정의합니다. 

그 다음, Graphic을 구현하는 나머지 Picture(Composite) 와 Line, Rectangle, Text (Leaf)를 생성합니다.

 

 

여기서 Composite인 Picture 객체를 보면 AddRemove, GetChild 메소드를 확인할 수 있는데요.

트리 구조를 구성하기 위해 특별한 경우가 아니라면 반드시 필요합니다.

 

더불어, 복합체 구조를 사용하는 목적이 되는 메소드 Draw(Operation)가 있습니다.

위의 UML을 확인해보면, forall g in graphic 으로 모든 자식 노드를 순회함을 확인할 수 있습니다.

 

위의 내용을 코드로 표현할 때 어떻게 짜야할지 감이 오시나요?

함께 코드로 확인해보도록 하겠습니다.

 

 

Structure

 

[GoF Design Patterns] - Composite Class Diagram

 

Component

- 구성 요소 객체들의 인터페이스를 선언합니다. (Composite, Leaf)

- 모든 클래스에 공통되는 Add, Remove, Operation 등 기본 동작을 적절히 구현합니다.

- 또, GetChild와 같이 하위 구성 요소에 액세스하고 관리하기 위한 인터페이스를 선언합니다.
- 선택적으로, 구성 요소의 부모에 액세스하기 위한 인터페이스를 정의하고 구현할 수도 있습니다.

 

Leaf

- 구성 요소 중에서 Leaf 객체를 나타내며, Leaf에는 자식을 저장하지 않습니다.

- Leaf 객체에는 복합체 객체에서 다룰 원시 객체에 대한 동작들을 정의합니다.

 

 

Composite

- 자식을 저장하며, 각 자식 구성 요소의 동작을 정의합니다.
- Component  인터페이스에서 상속받은 자식 구성요소와 관련된 작업을 구현합니다.

 

 

Client

- Component 인터페이스를 통해 composition 객체를 조작합니다.

 

 

 

Sample Code: Java

위에서 예시를 들었던 FileSystem을 생각해봅시다.

 

Folder에는 Folder나 File같은 자식 구성요소가 들어갈 수 있고, 

File 구성요소는 자식을 가지지 않습니다.

 

어떻게 구성될지 직접 코드로 확인해보도록 하겠습니다.

 

 

Component - FileSystem

public interface FileSystem {
    public int getSize();
    public void remove();
}

 

Leaf  - File

public class File implements FileSystem {
    private String name;
    private int size;

    public File(String _name, int _size) {
        this.name = _name;
        this.size = _size;
    }

    @Override
    public int getSize() {
        System.out.println(name + " 파일 크기 " + size);
        return size;
    }

    @Override
    public void remove() {
        System.out.println(name + " 파일 삭제");
    }
}

 

 

Composite - Folder

import java.util.ArrayList;

public class Folder implements FileSystem {
    private String name;
    private ArrayList<FileSystem> includes = new ArrayList<>();

    public Folder(String _name) {
        this.name = _name;
    }

    public void add(FileSystem fileSystem) {
        includes.add(fileSystem);
    }

    @Override
    public int getSize() {
        int total = 0;
        for (FileSystem include: includes) {
            total += include.getSize();
        }
        System.out.println(name + " 폴더 크기 : "+ total);
        System.out.println("- - - - -");
        return total;
    }

    @Override
    public void remove() {
        for (FileSystem include: includes) {
            include.remove();
        }
        System.out.println(name + " 폴더 삭제");
        System.out.println("- - - - -");
    }
}

 

 

Client

public class Client {
    public static void main(String[] args) {
        Folder rootFolder = new Folder("School");

//      1 학년
        Folder folder1 = new Folder("freshman");
        File assign1 = new File("picture", 256);
        
        rootFolder.add(folder1);
        folder1.add(assign1);


//      2 학년
        Folder folder2 = new Folder("sophomore");
        rootFolder.add(folder2);

//      2 학년 1 학기
        Folder sem1Folder = new Folder("1semester");
        Folder lecture1 = new Folder("C programming");
        File lecture2 = new File("Python", 520);
        
        File assign3 = new File("array", 266);
        File assign4 = new File("pointer", 302);

        sem1Folder.add(lecture1);
        sem1Folder.add(lecture2);
        lecture1.add(assign3);
        lecture1.add(assign4);

//      2 학년 2 학기
        Folder sem2Folder = new Folder("2semester");

        folder2.add(sem1Folder);
        folder2.add(sem2Folder);

        rootFolder.getSize();
    }
}

 

 

1학년과 2학년의 수업 자료를 담는 파일 구조를 표현합니다.

위의 코드를 실행하면 아래와 같은 출력을 확인할 수 있습니다.

 

 

picture 파일 크기 256
freshman 폴더 크기 : 256
- - - - -
array 파일 크기 266
pointer 파일 크기 302
C programming 폴더 크기 : 568
- - - - -
Python 파일 크기 520
1semester 폴더 크기 : 1088
- - - - -
2semester 폴더 크기 : 0
- - - - -
sophomore 폴더 크기 : 1088
- - - - -
School 폴더 크기 : 1344
- - - - -

 

 

Pros & Cons

Pros

- 복합체 패턴은 트리를 추가하거나 이동, 삭제하며 전체적인 구조를 유지하는 데 매우 유용합니다. 트리 구조의 재귀적인 특징을 잘 응용하는 것이라고 볼 수 있습니다.

- 복합체 패턴은 참조 투명성referencial transparency을 이용해 클라이언트의 사용을 단순화할 수 있습니다.  참조 투명성으로 if 문을 사용하지 않고도 Composite 와 leaf를 판단할 수 있습니다.

- 단일 객체와 복합 객체(그룹)를 동일하게 여기기 때문에 묶어서 연산하거나 관리할 때 편리합니다.

- 복합체 패턴은 설계의 범용성이 뛰어난데, 예를 들어 수평적, 수직적 모든 방향으로 객체를 확장할 수 있습니다.

 

 

* referencial transparency; 프로그램 동작의 변경없이 관련 값을 대체할 수 있다면 표현식을 참조 상 투명하다고 표현합니다.

 

 

Cons

- 수평적 방향으로만 확장이 가능하도록 Leaf를 제한하는 Composite를 만들기는 어렵습니다.

-  재귀호출의 특징 상 디버깅이 어렵습니다. 트리의 Depth가 깊어질 수록 라인 단위의 디버깅에 어려움이 생깁니다.

 

 

 

관련 패턴

C- Creational Patterns  |  S - Structural Patterns  |  B - Behavioral Patterns

 

 

B: Chain of Responsibility

복합체와 유사한 패턴으로 체인 패턴Chain pattern이 있다. 체인 패턴은 다음 객체를 호출하기 위해 부모 자식 관계를 갖고 있습니다.

 

 

B: Command

명령 패턴에서 객체를 실행할 때 복합체 패턴을 응용합니다.

 

 

S: Decorator

복합체 패턴은 장식자 패턴과 같이 응용합니다.

장식자 패턴도 새로운 기능을 확장하는 과정에서 재귀적인 결합이 이르어지는데 이는 복합테의 재귀적 모습과 비슷합니다.

 

차이점은 객체의 확장 유형이 약간 다르다는 점인데,

장식자는 상하 구조로 확장되지만 복합체는 상하, 좌우 형태로 확장됩니다.

 

 

 

 

 

 

 

그럼 지금까지 Composite Pattern에 대해 알아보았습니다.

오타나 잘못된 내용이 있다면 댓글로 남겨주세요!

감사합니다 ☺️ 

 

 

 

모든 Design Patterns 모아보기

 

Design Patterns

안녕하세요. GoF 디자인 패턴을 정리하고자 합니다. 디자인 패턴은 일주일 전부터 공부를 시작했는데, 스스로 설명하듯 적는게 익히는데 도움이 클 것같아 정말 오랜만에 시리즈로 포스팅하려

gngsn.tistory.com

 

 

반응형

'ETC > Design Patterns' 카테고리의 다른 글

Design Pattern, Command  (0) 2022.02.06
Design Pattern, Decorator  (0) 2022.02.03
Design Pattern, Singleton  (0) 2022.01.26
Design Pattern, Mediator  (0) 2022.01.24
Design Pattern, Adapter  (0) 2022.01.23