Menu
Coddy logo textTech

컴포지트 패턴

Coddy PHP 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 91개 중 80번째.

Composite 패턴은 객체를 트리 구조로 구성한 다음 이러한 구조를 개별 객체인 것처럼 다룰 수 있게 해 주는 구조적 디자인 패턴입니다. 단일 객체와 객체 그룹을 일관되게 처리하려는 부분-전체 계층 구조를 나타내는 데 적합합니다.

파일 시스템을 생각해 봅시다. folder는 file과 다른 folder를 포함할 수 있습니다.

전체 크기를 계산할 때는 file과 folder 모두에서 동일한 메서드를 호출하고 싶으며, folder는 그 콘텐츠의 크기를 재귀적으로 합산해야 합니다. Composite 패턴은 리프(file)와 composites(folder)에 동일한 인터페이스를 제공하므로 이 작업을 원활하게 만들어 줍니다.

<?php
interface FileSystemItem {
    public function getName(): string;
    public function getSize(): int;
}

class File implements FileSystemItem {
    public function __construct(
        private string $name,
        private int $size
    ) {}
    
    public function getName(): string {
        return $this->name;
    }
    
    public function getSize(): int {
        return $this->size;
    }
}

class Folder implements FileSystemItem {
    private array $items = [];
    
    public function __construct(private string $name) {}
    
    public function getName(): string {
        return $this->name;
    }
    
    public function add(FileSystemItem $item): void {
        $this->items[] = $item;
    }
    
    public function getSize(): int {
        $total = 0;
        foreach ($this->items as $item) {
            $total += $item->getSize();
        }
        return $total;
    }
}

이제 트리 구조를 만들고 이를 일관되게 다룰 수 있습니다:

<?php
$docs = new Folder("Documents");
$docs->add(new File("resume.pdf", 250));
$docs->add(new File("cover.docx", 50));

$images = new Folder("Images");
$images->add(new File("photo.jpg", 1500));

$root = new Folder("Root");
$root->add($docs);
$root->add($images);
$root->add(new File("readme.txt", 10));

echo $root->getSize();

출력:

1810

클라이언트 코드는 단일 file을 다루든 깊게 중첩된 folder 구조를 다루든 상관없이 getSize()를 호출합니다. 이 패턴은 재귀를 내부적으로 처리하므로 복잡한 계층 구조를 쉽게 다룰 수 있습니다.

challenge icon

챌린지

쉬움

Composite 패턴을 사용하여 조직도 시스템을 만들어 봅시다. Company는 department가 employee와 다른 하위 department를 contain할 수 있는 계층 구조를 가집니다. 이는 전체 조직에서 totals를 uniform하게 계산하려는 경우에 적합한 tree structure입니다.

코드를 네 개의 파일로 구성합니다:

  • OrganizationUnit.php: 개별 employee와 department가 모두 구현할 OrganizationUnit interface를 정의합니다. 이 interface에는 두 가지 method인 getName(): stringgetSalary(): int가 있어야 합니다. 이 common interface를 사용하면 costs를 계산할 때 single employee와 전체 department를 동일한 방식으로 다룰 수 있습니다.
  • Employee.php: OrganizationUnit interface를 include하고 이를 구현하는 Employee class를 만듭니다. employee는 organization tree의 leaf node입니다. name과 salary를 가지지만 다른 unit을 contain할 수는 없습니다. constructor promotion을 사용하여 name과 salary를 받고, 두 interface method를 모두 구현합니다.
  • Department.php: OrganizationUnit interface를 include하고 이를 구현하는 Department class를 만듭니다. department는 employee와 다른 department를 모두 contain할 수 있는 composite입니다. department는 다음과 같아야 합니다:
    • constructor에서 name을 accept합니다
    • getName()을 구현하여 department의 name을 return합니다
    • employee 또는 하위 department를 add하기 위한 add(OrganizationUnit $unit): void method를 가집니다
    • getSalary()를 구현하여 contain된 모든 unit의 total salary를 return합니다(내부의 모든 항목을 recursively summing)
  • main.php: Employee 및 Department 파일을 include합니다. organization structure를 나타내는 JSON string 하나를 input으로 받습니다.

    JSON은 다음 format입니다:

    {"Engineering": [{"name": "Alice", "salary": 80000}, {"name": "Bob", "salary": 75000}], "Sales": [{"name": "Carol", "salary": 65000}]}

    각 key는 department name이고, 그 value는 name과 salary가 포함된 employee의 array입니다.

    "Company"라고 called된 root Department를 Create합니다. JSON의 각 department에 대해 Department를 Create하고, 해당 employee를 add한 다음 그 department를 Company에 add합니다. 마지막으로 다음 format으로 전체 Company salary를 print합니다:

    Total Salary: [amount]

Composite Pattern의 장점은 Company department에서 getSalary()를 호출하면 모든 nested department와 employee의 합계가 자동으로 계산된다는 것입니다. 따라서 client code는 내부 structure를 알 필요가 없습니다.

직접 해보기

<?php
require_once 'Employee.php';
require_once 'Department.php';

// JSON 입력을 읽습니다
$input = trim(fgets(STDIN));
$data = (array)json_decode($input, true);

// TODO: "Company"라는 루트 Department 생성

// TODO: JSON 데이터의 각 department를 순회
// 각 department에 대해:
//   - 새 Department 객체 생성
//   - 각 employee를 해당 department에 추가
//   - department를 Company에 추가

// TODO: 다음 형식으로 총 급여 출력: Total Salary: [amount]

?>
quiz icon실력 점검

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

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

직접 연습해 보세요: 온라인 PHP 컴파일러