본문 바로가기
Thinking/Concept

컴포지트 패턴(Composite Pattern) 이해하기

by Dev_카페인 2024. 11. 15.
반응형

컴포지트 패턴(Composite Pattern) 이해하기

컴포지트 패턴객체들을 트리 구조로 구성하여 단일 객체와 복합 객체를 동일하게 처리할 수 있도록 하는 구조적 디자인 패턴입니다. 이 패턴은 특히 부분-전체 계층 구조를 구현할 때 유용하며, 복합 객체를 개별 객체처럼 다룰 수 있게 해줍니다. 컴포지트 패턴을 활용하면 클라이언트 코드가 트리 구조를 고려하지 않고도 일관된 방식으로 작업을 처리할 수 있습니다.

이번 포스트에서는 컴포지트 패턴의 개념과 구조, 구현 방법, 장단점, 사용 시점 등을 구체적인 예제와 함께 살펴보겠습니다.

컴포지트 패턴이란?

컴포지트 패턴은 객체들을 트리 구조로 구성하여 부분-전체 관계를 표현할 수 있는 디자인 패턴입니다. 컴포지트 패턴을 사용하면 개별 객체와 복합 객체를 동일하게 처리할 수 있으며, 이를 통해 계층적 구조를 단순화할 수 있습니다. 이 패턴은 단일 객체와 복합 객체 간의 일관성을 제공하며, 특히 파일 시스템이나 UI 컴포넌트 트리 구조와 같은 경우에 자주 사용됩니다.

 

컴포지트 패턴의 구조

컴포지트 패턴은 다음과 같은 구성 요소로 이루어집니다.

  1. Component (구성 요소): 단일 객체와 복합 객체를 동일하게 다룰 수 있는 인터페이스를 정의합니다. 공통 메서드나 속성을 정의하여, 클라이언트가 일관된 방식으로 접근할 수 있도록 합니다.
  2. Leaf (단일 객체): Component 인터페이스를 구현하는 개별 객체로, 더 이상 하위 객체가 없는 노드입니다. 주로 실제 작업을 수행하며, 자식 요소가 없습니다.
  3. Composite (복합 객체): Component 인터페이스를 구현하며, 자식 Component를 포함할 수 있는 객체입니다. 자신이 포함한 자식 객체에 작업을 위임하거나, 트리 구조에서 일관되게 작업을 처리할 수 있도록 합니다.
  4. Client (클라이언트): Component 인터페이스를 사용하여 단일 객체와 복합 객체를 동일하게 처리합니다.

컴포지트(Composite)

컴포지트 패턴의 구현 방법

예제 코드: 파일 시스템 구조 예제

컴포지트 패턴의 대표적인 예로 파일 시스템을 들 수 있습니다. 폴더 안에 여러 파일이나 다른 폴더가 포함될 수 있는 계층적 구조에서, 폴더와 파일을 동일하게 다루는 예제를 구현해보겠습니다.

// Component (구성 요소) 인터페이스
public interface IFileSystemItem
{
    void Display(string indent);
}

// Leaf (단일 객체) 클래스
public class File : IFileSystemItem
{
    private string _name;

    public File(string name)
    {
        _name = name;
    }

    public void Display(string indent)
    {
        Console.WriteLine($"{indent}- { _name } (File)");
    }
}

// Composite (복합 객체) 클래스
public class Directory : IFileSystemItem
{
    private string _name;
    private List<IFileSystemItem> _items = new List<IFileSystemItem>();

    public Directory(string name)
    {
        _name = name;
    }

    public void Add(IFileSystemItem item)
    {
        _items.Add(item);
    }

    public void Remove(IFileSystemItem item)
    {
        _items.Remove(item);
    }

    public void Display(string indent)
    {
        Console.WriteLine($"{indent}+ { _name } (Directory)");
        foreach (var item in _items)
        {
            item.Display(indent + "   ");
        }
    }
}

// Client 코드
class Program
{
    static void Main()
    {
        // 파일 생성
        File file1 = new File("File1.txt");
        File file2 = new File("File2.txt");

        // 디렉터리 생성 및 파일 추가
        Directory rootDirectory = new Directory("Root");
        Directory subDirectory1 = new Directory("SubDir1");
        Directory subDirectory2 = new Directory("SubDir2");

        rootDirectory.Add(file1);
        rootDirectory.Add(subDirectory1);
        subDirectory1.Add(file2);
        rootDirectory.Add(subDirectory2);

        // 디렉터리 구조 출력
        rootDirectory.Display("");
    }
}

위 코드에서 IFileSystemItem은 Display() 메서드를 정의하여 파일과 폴더를 동일하게 처리할 수 있도록 하는 인터페이스입니다. File 클래스는 단일 파일을 나타내는 Leaf 객체로, 파일 이름을 출력합니다. Directory 클래스는 Composite 객체로서, 폴더에 포함된 자식 객체들을 저장하고, 자식 객체들을 순회하여 계층 구조를 출력합니다.

실행 결과는 다음과 같이 트리 구조로 출력됩니다.

+ Root (Directory)
   - File1.txt (File)
   + SubDir1 (Directory)
      - File2.txt (File)
   + SubDir2 (Directory)

이 예제에서 컴포지트 패턴을 통해 클라이언트는 개별 파일과 폴더 구조를 동일하게 다루고 있습니다. 파일 시스템의 각 요소는 IFileSystemItem 인터페이스를 통해 통일된 방식으로 처리됩니다.

 

컴포지트 패턴의 장단점

장점

  1. 일관된 트리 구조 관리: 단일 객체와 복합 객체를 동일하게 처리할 수 있어 일관성 있는 트리 구조 관리가 가능합니다.
  2. 객체의 재귀적 구성: 트리 구조를 통해 객체를 재귀적으로 구성할 수 있어 복합 객체 계층을 쉽게 구현할 수 있습니다.
  3. 확장성: 새로운 타입의 구성 요소를 쉽게 추가할 수 있어 확장성이 뛰어납니다.

단점

  1. 구조가 복잡해질 수 있음: 복합 객체 내에 많은 자식 객체가 포함될 경우 구조가 복잡해질 수 있습니다.
  2. 추상화의 한계: 트리 구조의 특정 계층에 특화된 작업이 필요할 경우, 공통 인터페이스로 인해 추상화의 한계가 있을 수 있습니다.

 

언제 컴포지트 패턴을 사용해야 할까?

컴포지트 패턴은 부분-전체 관계를 표현해야 하거나, 객체들을 트리 구조로 구성하여 단일 객체와 복합 객체를 동일하게 다뤄야 할 때 유용합니다. 다음과 같은 경우에 사용하면 좋습니다.

  1. 계층적 구조가 필요한 경우: 파일 시스템, 조직도, UI 컴포넌트 트리 등 트리 구조가 필요한 시스템에서 유용합니다.
  2. 개별 객체와 복합 객체를 동일하게 처리할 필요가 있는 경우: 클라이언트 코드가 복합 객체와 개별 객체를 구분하지 않고 일관된 인터페이스로 접근해야 할 때 사용합니다.
  3. 재귀적 구조를 구현할 때: 각 객체가 다른 객체들을 포함할 수 있는 재귀적 구조를 표현할 때 적합합니다.

 

컴포지트 패턴과 관련 패턴 비교

컴포지트 패턴은 트리 구조를 통해 부분-전체 관계를 표현하는 데 특화되어 있으며, 특히 프록시 패턴데코레이터 패턴과 함께 사용될 수 있습니다.

  • 프록시 패턴: 객체에 대한 접근을 제어하기 위해 대리 객체를 사용하는 패턴으로, 컴포지트 패턴과 함께 사용하여 복합 객체에 대한 접근을 제한할 수 있습니다.
  • 데코레이터 패턴: 객체에 새로운 기능을 추가할 때 사용하는 패턴으로, 컴포지트 패턴과 함께 사용하여 복합 객체에 동적으로 기능을 추가할 수 있습니다.

 

마무리하며

컴포지트 패턴은 트리 구조의 계층적 시스템을 표현하고자 할 때 매우 유용한 디자인 패턴입니다. 이를 통해 단일 객체와 복합 객체를 동일한 방식으로 처리할 수 있어, 클라이언트 코드가 트리 구조를 이해하지 않고도 일관된 방식으로 작업을 수행할 수 있습니다.

파일 시스템이나 UI 컴포넌트와 같이 계층 구조가 필요한 시스템에서 컴포지트 패턴을 활용해 보세요. 이 패턴을 통해 복잡한 트리 구조를 간단하게 관리하고, 코드의 유연성과 확장성을 높일 수 있습니다.

반응형