[디자인 패턴] 복합체 패턴_ Composite pattern (JavaScript)

2024. 11. 17. 21:45Web_Programming

 

복합체 패턴이란?

Composite Pattern은 객체들을 트리 구조로 구성하고 개별 객체와 동일하게 취급할 수 있도록 하는 구조적 디자인 패턴입니다. 파일 시스템과 같은 계층적 데이터 구조를 처리하는 데 유용하며, 단일 객체(Leaf)와 복합 객체(Composite)를 동일한 방식으로 다룰 수 있도록 설계됩니다. 아래는 설명을 보완한 내용입니다.

+처음에 정의만 봤을 때는 무슨말인지 하나도 몰랐습니다.. 구서요소 개념들 하나하나를 이해하는 것이 중요합니다!

구성 요소

  1. Component
    • LeafComposite 객체의 공통 인터페이스를 정의합니다.
    • 예를 들어 getSize, display와 같은 메서드가 정의됩니다.
  2. Leaf (단일 객체)
    • 실제 데이터를 가지며, 트리의 가장 말단을 구성합니다.
    • Component의 메서드를 구현하지만 복합 객체를 포함하지는 않습니다.
  3. Composite (복합 객체)
    • 여러 Component(Leaf 또는 Composite)를 포함할 수 있습니다.
    • Component 메서드를 재귀적으로 호출하여 전체 트리 구조를 탐색하거나 연산합니다.
    • 자식 객체를 추가하거나 제거하는 메서드(add, remove)를 제공합니다.

동작 원리

  1. 재귀적 호출
    Composite 객체는 자신이 포함하고 있는 모든 Component를 순회하며 작업을 수행합니다.
  2. 단일 객체와 복합 객체의 동일한 취급
    LeafComposite는 같은 인터페이스를 따르기 때문에 클라이언트는 객체가 단일 객체인지 복합 객체인지 신경 쓰지 않아도 됩니다.

예시 코드

가장 대표적인 복합체 패턴 활용 예시이자 코드는 파일 시스템입니다.

class File {
  constructor(name, size) {
    this.name = name;
    this.size = size;
  }

  getSize() {
    return this.size;
  }

  getName() {
    return this.name;
  }

  display(indent = '') {
    console.log(`${indent}${this.name} (${this.size} bytes)`);
  }
}

class Directory {
  constructor(name) {
    this.name = name;
    this.contents = [];
  }

  add(item) {
    this.contents.push(item);
  }

  remove(item) {
    this.contents = this.contents.filter(content => content !== item);
  }

  getSize() {
    return this.contents.reduce((total, content) => total += content.getSize(), 0);
  }

  getName() {
    return this.name;
  }

  display(indent = '') {
    console.log(`${indent}${this.name}/`);
    this.contents.forEach(content => content.display(indent + '  '));
  }
}
// 파일 생성
const file1 = new File('file1.txt', 100);
const file2 = new File('file2.txt', 200);
const file3 = new File('file3.txt', 300);

// 디렉토리 생성
const dir1 = new Directory('dir1');
const dir2 = new Directory('dir2');
const root = new Directory('root');

// 파일을 디렉토리에 추가
dir1.add(file1);
dir1.add(file2);
dir2.add(file3);

// 디렉토리를 루트 디렉토리에 추가
root.add(dir1);
root.add(dir2);

// 전체 파일 시스템의 크기 출력
console.log(`Total size: ${root.getSize()} bytes`);

// 파일 시스템 구조 출력
root.display();
  • dir1에 file1을 추가하면, dir1.display() 호출 시 file1.display()가 재귀적으로 호출됩니다.
  • dir1.getSize()를 호출하면 내부의 모든 파일 크기를 합산하여 반환합니다.
console.log(root.getSize()); 
// dir1의 모든 파일 크기와 dir2의 모든 파일 크기를 합산
  • 위의 코드처럼 재귀적인 데이터 구조를 효율적으로 처리할 수 있는 강력한 패턴입니다.

 

사용 사례

  1. 파일 시스템
    • 파일은 Leaf이고 폴더는 Composite로 취급되어 전체 구조를 표현합니다.
  2. GUI 컴포넌트
    • 버튼, 텍스트 박스 등은 Leaf로, 패널, 창 등은 Composite로 간주됩니다.
  3. 조직도
    • 개별 직원은 Leaf, 부서나 팀은 Composite로 처리할 수 있습니다.

장점

  • 일관된 인터페이스 제공
    • 클라이언트는 단일 객체와 복합 객체를 구분할 필요가 없습니다.
  • 유연성
    • 트리 구조를 쉽게 확장하거나 변경할 수 있습니다.
  • 재사용성 증가
    • 동일한 로직을 복합 객체와 단일 객체 모두에 사용할 수 있습니다.

단점

  • 복잡성 증가
    • 계층 구조가 깊어질수록 코드의 디버깅과 유지보수가 어려워질 수 있습니다.
  • 객체 간 의존성 증가
    • 복합 객체는 자식 객체와 강하게 연결되어 관리가 어려워질 수 있습니다.

 

반응형