Menu
Coddy logo textTech

Patrón Composite

Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 80 de 91.

El Patrón Composite es un patrón de diseño estructural que permite componer objetos en estructuras de árbol y luego trabajar con estas estructuras como si fueran objetos individuales. Es perfecto para representar jerarquías de parte-todo donde se desea tratar a los objetos individuales y a los grupos de objetos de manera uniforme.

Considere un sistema de archivos. Una carpeta puede contener archivos y otras carpetas.

Cuando calculas el tamaño total, quieres llamar al mismo método tanto en archivos como en carpetas, y las carpetas deben sumar recursivamente su contenido. El Patrón Composite hace que esto sea fluido al darles tanto a las hojas (archivos) como a los compuestos (carpetas) la misma interfaz.

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

Ahora puedes construir estructuras de árbol y tratarlas de manera 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();

Resultado:

1810

El código cliente llama a getSize() sin preocuparse de si está tratando con un solo archivo o con una estructura de carpetas profundamente anidada. El patrón maneja la recursividad internamente, facilitando el trabajo con jerarquías complejas.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de organigrama utilizando el Patrón Composite. Las empresas tienen una estructura jerárquica donde los departamentos pueden contener empleados y otros subdepartamentos — una estructura de árbol perfecta donde se desea calcular totales de manera uniforme en toda la organización.

Organizarás tu código en cuatro archivos:

  • OrganizationUnit.php — Define una interfaz OrganizationUnit que implementarán tanto los empleados individuales como los departamentos. Debe tener dos métodos: getName(): string y getSalary(): int. Esta interfaz común te permite tratar a los empleados individuales y a departamentos enteros de la misma manera al calcular costos.
  • Employee.php — Incluye la interfaz OrganizationUnit y crea una clase Employee que la implemente. Un empleado es un nodo hoja en tu árbol de organización — tiene un nombre y un salario, pero no puede contener otras unidades. Usa la promoción de constructor para aceptar el nombre y el salario, e implementa ambos métodos de la interfaz.
  • Department.php — Incluye la interfaz OrganizationUnit y crea una clase Department que la implemente. Un departamento es un compuesto que puede contener tanto empleados como otros departamentos. Tu departamento debe:
    • Aceptar un nombre en su constructor
    • Tener un método add(OrganizationUnit $unit): void para añadir empleados o subdepartamentos
    • Implementar getSalary() para devolver el salario total de todas las unidades que contiene (sumando recursivamente todo lo que hay dentro)
  • main.php — Incluye los archivos Employee y Department. Recibirás una entrada: una cadena JSON que representa la estructura de la organización.

    El JSON tendrá este formato:

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

    Cada clave es el nombre de un departamento, y su valor es un array de empleados con sus nombres y salarios.

    Crea un Department raíz llamado "Company". Para cada departamento en el JSON, crea un Department, añade sus empleados, luego añade ese departamento a la Company. Finalmente, imprime el salario total de la empresa en este formato:

    Total Salary: [amount]

La belleza del Patrón Composite es que llamar a getSalary() en el departamento Company calcula automáticamente la suma de todos los departamentos y empleados anidados — el código del cliente no necesita conocer la estructura interna.

Hoja de referencia

El Patrón Composite es un patrón de diseño estructural que compone objetos en estructuras de árbol y permite tratar objetos individuales y grupos de manera uniforme.

Es ideal para jerarquías de parte-todo donde se desea la misma interfaz tanto para los nodos hoja como para los nodos compuestos.

Componentes Clave

  • Interfaz de Componente — Define operaciones comunes tanto para hojas como para compuestos
  • Hoja (Leaf) — Representa objetos individuales que no pueden contener otros objetos
  • Compuesto (Composite) — Contiene hijos (hojas u otros compuestos) e implementa operaciones delegando en los hijos

Ejemplo: Sistema de Archivos

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

El cliente llama a getSize() de manera uniforme tanto en archivos como en carpetas. El compuesto maneja la recursividad internamente, haciendo que las jerarquías complejas sean fáciles de manejar.

Pruébalo tú mismo

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

// Leer la entrada JSON
$input = trim(fgets(STDIN));
$data = (array)json_decode($input, true);

// TODO: Crear un Departamento raíz llamado "Company"

// TODO: Recorrer cada departamento en los datos JSON
// Para cada departamento:
//   - Crear un nuevo objeto Department
//   - Añadir cada empleado a ese departamento
//   - Añadir el departamento a la Company

// TODO: Imprimir el salario total en el formato: Total Salary: [amount]

?>
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos