Menu
Coddy logo textTech

컴포지트 패턴

Coddy Dart 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 110개 중 99번째.

컴포지트 패턴(Composite pattern)은 개별 객체와 객체 그룹을 균일하게 다룰 수 있게 해줍니다. 이 패턴은 객체들을 트리 구조로 구성하여, 단일 요소와 요소의 집합이 모두 동일한 인터페이스를 공유하도록 합니다. 이는 파일 시스템, 조직도 또는 UI 컴포넌트와 같은 계층 구조를 표현하는 데 적합합니다.

이 패턴에는 두 가지 유형의 참여자가 있습니다: Leaf 노드(자식이 없는 개별 객체)와 Composite 노드(다른 컴포넌트를 포함하는 컨테이너)입니다. 둘 다 공통 Component 인터페이스를 구현합니다:

abstract class FileSystemItem {
  String get name;
  int getSize();
  void display([String indent = '']);
}

class File implements FileSystemItem {
  @override
  final String name;
  final int size;

  File(this.name, this.size);

  @override
  int getSize() => size;

  @override
  void display([String indent = '']) {
    print('$indent- $name ($size KB)');
  }
}

class Folder implements FileSystemItem {
  @override
  final String name;
  final List<FileSystemItem> _children = [];

  Folder(this.name);

  void add(FileSystemItem item) => _children.add(item);

  @override
  int getSize() => _children.fold(0, (sum, item) => sum + item.getSize());

  @override
  void display([String indent = '']) {
    print('$indent+ $name/');
    for (var child in _children) {
      child.display('$indent  ');
    }
  }
}

void main() {
  var docs = Folder('Documents');
  docs.add(File('resume.pdf', 150));
  docs.add(File('photo.jpg', 2400));

  var projects = Folder('Projects');
  projects.add(File('main.dart', 25));
  docs.add(projects);

  docs.display();
  print('Total: ${docs.getSize()} KB');
}

Folder 클래스는 File 객체와 다른 Folder 객체를 모두 포함할 수 있습니다. 폴더에서 getSize()를 호출하면, 모든 내용물의 전체 크기를 재귀적으로 계산합니다. 클라이언트 코드는 단일 파일로 작업하는지 아니면 전체 폴더 계층 구조로 작업하는지 알 필요가 없습니다. 둘 다 동일한 메서드에 응답하기 때문입니다.

challenge icon

챌린지

쉬움

Composite 패턴을 사용하여 조직도 시스템을 구축해 봅시다! 개별 직원과 부서(직원 및 다른 부서를 포함하는)를 동일하게 취급할 수 있는 구조를 만들게 됩니다. 이는 회사의 계층 구조를 표현하는 데 완벽한 방식입니다.

코드는 다음 두 개의 파일로 구성됩니다:

  • organization.dart: 이 파일에는 컴포넌트 인터페이스와 leaf 및 composite 클래스가 포함됩니다. name getter, int를 반환하는 getSalaryBudget() 메서드, 그리고 계층 구조를 보여주기 위한 display([String indent = '']) 메서드를 가진 추상 클래스 OrganizationUnit을 생성하세요. OrganizationUnit을 구현하는 Employee 클래스(leaf)를 만드세요. 이 클래스는 이름과 급여를 가져야 하며, getSalaryBudget()은 직원의 급여를 반환해야 합니다. display() 메서드는 [indent]- [name] ($[salary]) 형식을 출력해야 합니다. 그 다음, 역시 OrganizationUnit을 구현하는 Department 클래스(composite)를 생성하세요. 이 클래스는 이름을 가져야 하며 OrganizationUnit 자식들의 리스트를 유지해야 합니다. 직원이나 하위 부서를 포함할 수 있도록 add() 메서드를 추가하세요. getSalaryBudget()은 모든 자식의 예산을 재귀적으로 합산해야 합니다. display()[indent]+ [name]/를 출력한 후, 들여쓰기를 늘려(공백 두 개 추가) 각 자식을 표시해야 합니다.
  • main.dart: 조직 파일을 임포트하고 회사 구조를 구축하세요. 급여가 80000Alice와 급여가 75000Bob, 두 명의 직원을 포함하는 Engineering 부서를 만드세요. 급여가 65000Carol 한 명의 직원을 포함하는 QA 부서를 만드세요. 이제 Technology 부서를 만들고 EngineeringQA를 하위 부서로 추가하세요. Technology 부서에서 display()를 호출한 다음, Total Budget: $와 함께 전체 급여 예산을 출력하세요.

Composite 패턴의 장점은 Technology.getSalaryBudget()이 모든 중첩된 부서와 직원의 합계를 자동으로 계산한다는 것입니다. 클라이언트 코드는 내부 구조를 알 필요가 없습니다!

예상 출력:

+ Technology/
  + Engineering/
    - Alice ($80000)
    - Bob ($75000)
  + QA/
    - Carol ($65000)
Total Budget: $220000

치트 시트

컴포지트 패턴(Composite pattern)은 객체들을 트리 구조로 구성하여 단일 요소와 컬렉션이 동일한 인터페이스를 공유하게 함으로써, 개별 객체와 객체 그룹을 균일하게 다룰 수 있도록 합니다.

이 패턴은 다음과 같이 구성됩니다:

  • 컴포넌트(Component): 공통 작업을 정의하는 추상 인터페이스
  • 리프(Leaf): 자식이 없는 개별 객체
  • 컴포지트(Composite): 다른 컴포넌트(리프 또는 컴포지트)를 포함하는 컨테이너

예제: 파일 시스템

abstract class FileSystemItem {
  String get name;
  int getSize();
  void display([String indent = '']);
}

class File implements FileSystemItem {
  @override
  final String name;
  final int size;

  File(this.name, this.size);

  @override
  int getSize() => size;

  @override
  void display([String indent = '']) {
    print('$indent- $name ($size KB)');
  }
}

class Folder implements FileSystemItem {
  @override
  final String name;
  final List<FileSystemItem> _children = [];

  Folder(this.name);

  void add(FileSystemItem item) => _children.add(item);

  @override
  int getSize() => _children.fold(0, (sum, item) => sum + item.getSize());

  @override
  void display([String indent = '']) {
    print('$indent+ $name/');
    for (var child in _children) {
      child.display('$indent  ');
    }
  }
}

void main() {
  var docs = Folder('Documents');
  docs.add(File('resume.pdf', 150));
  docs.add(File('photo.jpg', 2400));

  var projects = Folder('Projects');
  projects.add(File('main.dart', 25));
  docs.add(projects);

  docs.display();
  print('Total: ${docs.getSize()} KB');
}

Folder 클래스는 File 객체와 다른 Folder 객체를 모두 포함할 수 있습니다. 폴더에서 getSize()를 호출하면 모든 내용의 총 크기를 재귀적으로 계산합니다. 클라이언트 코드는 개별 파일과 전체 폴더 계층 구조를 균일하게 처리합니다.

직접 해보기

import 'organization.dart';

void main() {
  // TODO: Engineering 부서 생성
  // TODO: 급여 80000인 Alice 추가
  // TODO: 급여 75000인 Bob 추가
  
  // TODO: QA 부서 생성
  // TODO: 급여 65000인 Carol 추가
  
  // TODO: Technology 부서 생성
  // TODO: Engineering과 QA를 하위 부서로 추가
  
  // TODO: Technology 부서에서 display() 호출
  
  // TODO: "Total Budget: $"와 전체 급여 예산을 이어서 출력
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨