컴포지트 패턴
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()를 호출하면, 모든 내용물의 전체 크기를 재귀적으로 계산합니다. 클라이언트 코드는 단일 파일로 작업하는지 아니면 전체 폴더 계층 구조로 작업하는지 알 필요가 없습니다. 둘 다 동일한 메서드에 응답하기 때문입니다.
챌린지
쉬움Composite 패턴을 사용하여 조직도 시스템을 구축해 봅시다! 개별 직원과 부서(직원 및 다른 부서를 포함하는)를 동일하게 취급할 수 있는 구조를 만들게 됩니다. 이는 회사의 계층 구조를 표현하는 데 완벽한 방식입니다.
코드는 다음 두 개의 파일로 구성됩니다:
organization.dart: 이 파일에는 컴포넌트 인터페이스와 leaf 및 composite 클래스가 포함됩니다.namegetter,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: 조직 파일을 임포트하고 회사 구조를 구축하세요. 급여가80000인Alice와 급여가75000인Bob, 두 명의 직원을 포함하는Engineering부서를 만드세요. 급여가65000인Carol한 명의 직원을 포함하는QA부서를 만드세요. 이제Technology부서를 만들고Engineering과QA를 하위 부서로 추가하세요. 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: $"와 전체 급여 예산을 이어서 출력
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.