Menu
Coddy logo textTech

Padrão Composite

Parte da seção Programação Orientada a Objetos do Journey de PHP da Coddy — lição 80 de 91.

O Composite Pattern é um padrão de projeto estrutural que permite compor objetos em estruturas de árvore e, em seguida, trabalhar com essas estruturas como se fossem objetos individuais. Ele é perfeito para representar hierarquias parte-todo, onde você deseja tratar objetos únicos e grupos de objetos de forma uniforme.

Considere um sistema de arquivos. Uma pasta pode conter arquivos e outras pastas.

Quando você calcula o tamanho total, você deseja chamar o mesmo método tanto em arquivos quanto em pastas, e as pastas devem somar recursivamente seus conteúdos. O Padrão Composite torna isso transparente ao fornecer a mesma interface tanto para as folhas (arquivos) quanto para os compostos (pastas).

<?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;
    }
}

Agora você pode construir estruturas de árvore e tratá-las de forma uniforme:

<?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();

Saída:

1810

O código cliente chama getSize() sem se preocupar se está lidando com um único arquivo ou uma estrutura de pastas profundamente aninhada. O padrão lida com a recursão internamente, tornando as hierarquias complexas fáceis de trabalhar.

challenge icon

Desafio

Fácil

Vamos construir um sistema de organograma usando o Composite Pattern. As empresas possuem uma estrutura hierárquica onde os departamentos podem conter funcionários e outros subdepartamentos — uma estrutura de árvore perfeita onde você deseja calcular totais de forma uniforme em toda a organização.

Você organizará seu código em quatro arquivos:

  • OrganizationUnit.php — Defina uma interface OrganizationUnit que tanto funcionários individuais quanto departamentos implementarão. Ela deve ter dois métodos: getName(): string e getSalary(): int. Esta interface comum permite que você trate funcionários individuais e departamentos inteiros da mesma maneira ao calcular custos.
  • Employee.php — Inclua a interface OrganizationUnit e crie uma classe Employee que a implemente. Um funcionário é um nó folha em sua árvore organizacional — ele tem um nome e um salário, mas não pode conter outras unidades. Use a promoção de construtor para aceitar o nome e o salário, e implemente ambos os métodos da interface.
  • Department.php — Inclua a interface OrganizationUnit e crie uma classe Department que a implemente. Um departamento é um composto que pode conter tanto funcionários quanto outros departamentos. Seu departamento deve:
    • Aceitar um nome em seu construtor
    • Ter um método add(OrganizationUnit $unit): void para adicionar funcionários ou subdepartamentos
    • Implementar getSalary() para retornar o salário total de todas as unidades que ele contém (somando recursivamente tudo o que estiver dentro)
  • main.php — Inclua os arquivos Employee e Department. Você receberá uma entrada: uma string JSON representando a estrutura da organização.

    O JSON estará neste formato:

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

    Cada chave é o nome de um departamento, e seu valor é um array de funcionários com seus nomes e salários.

    Crie um Department raiz chamado "Company". Para cada departamento no JSON, crie um Department, adicione seus funcionários e, em seguida, adicione esse departamento à Company. Por fim, imprima o salário total da empresa neste formato:

    Total Salary: [amount]

A beleza do Composite Pattern é que chamar getSalary() no departamento Company calcula automaticamente a soma de todos os departamentos e funcionários aninhados — o código do cliente não precisa conhecer a estrutura interna.

Folha de consulta

O Composite Pattern é um padrão de design estrutural que compõe objetos em estruturas de árvore e permite tratar objetos individuais e grupos de forma uniforme.

É ideal para hierarquias do tipo parte-todo, onde você deseja a mesma interface tanto para nós folha quanto para nós compostos.

Componentes Principais

  • Interface Component — Define operações comuns tanto para folhas quanto para compostos
  • Leaf (Folha) — Representa objetos individuais que não podem conter outros objetos
  • Composite (Composto) — Contém filhos (folhas ou outros compostos) e implementa operações delegando-as aos filhos

Exemplo: Sistema de Arquivos

<?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;
    }
}

Uso

<?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

O cliente chama getSize() uniformemente tanto em arquivos quanto em pastas. O composto lida com a recursão internamente, tornando hierarquias complexas simples de trabalhar.

Experimente você mesmo

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

// Lê a entrada JSON
$input = trim(fgets(STDIN));
$data = (array)json_decode($input, true);

// TODO: Criar um Departamento raiz chamado "Company"

// TODO: Percorrer cada departamento nos dados JSON
// Para cada departamento:
//   - Criar um novo objeto Department
//   - Adicionar cada funcionário a esse departamento
//   - Adicionar o departamento à Company

// TODO: Imprimir o salário total no formato: Total Salary: [amount]

?>
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos