09-02-2026

Patrón composite en php: qué es y cómo implementarlo

Después de explicar el Patrón Composite quiero poner un ejemplo en php.
  • Primero se crearía un interfaz composite:
    //b>
    /**
     * INTERFAZ COMPONENTE (Component)
     * 
     * Define la interfaz común que deben implementar tanto las hojas (ítems simples)
     * como los compuestos (grupos/menús con subelementos).
     * 
     * Gracias a esta interfaz, el cliente puede tratar a todos los elementos del árbol
     * de forma uniforme, sin importar si son hojas o compuestos.
     */
    interface MenuComponent
    {
    /**
     * Renderiza el elemento (y sus hijos si los tiene) como texto o HTML.
     * El parámetro $level ayuda a crear indentación visual para ver la jerarquía.
     */
    public function render(int $level = 0): string;
    
    /**
     * Devuelve el nombre del elemento (útil para mostrar, depurar o buscar).
     */
    public function getName(): string;
    }
    
    /**
     * HOJA (Leaf)
     * 
     * Representa un elemento final del árbol: un ítem de menú simple
     * que NO tiene hijos (no puede contener otros menús).
     * 
     * Implementa directamente la interfaz sin lógica de contenedor.
     */
    final readonly class MenuItem implements MenuComponent
    {
    /**
     * Constructor con promoción de propiedades (PHP 8.0+)
     * - readonly → no se puede modificar después de creado (inmutabilidad)
     * - private → encapsulamiento (solo accesible mediante métodos)
     */
    public function __construct(
    private string $name,
    private string $url
    ) {}
    
    public function getName(): string
    {
    return $this->name;
    }
    
    /**
     * Render simple: solo muestra un enlace con indentación según nivel.
     * No tiene hijos → no hay recursión aquí.
     */
    public function render(int $level = 0): string
    {
    // str_repeat crea espacios para indentar visualmente la jerarquía
    $indent = str_repeat('  ', $level);
    
    // Formato básico de ítem de menú (puedes cambiar a 
  • si usas HTML real) return $indent . "- url}\">{$this->name}\n"; } } /** * COMPUESTO (Composite) * * Puede contener cero o más MenuComponent (hojas u otros compuestos). * Implementa la misma interfaz que las hojas → uniformidad. * * Aquí está la recursividad: cuando se llama a render(), * delega la renderización a todos sus hijos. */ final class MenuGroup implements MenuComponent { /** * Array tipado (PHP 8.1+): solo acepta objetos que implementen MenuComponent * @var list → indica que es una lista de elementos válidos */ private array $children = []; public function __construct( private string $name ) {} public function getName(): string { return $this->name; } /** * Añade un hijo al grupo (hoja o subgrupo). * No hay restricción de tipo más allá de la interfaz → polimorfismo. */ public function add(MenuComponent $component): void { $this->children[] = $component; } /** * Elimina un hijo específico (útil en editores dinámicos de menús). * Usa comparación estricta (===) para objetos. */ public function remove(MenuComponent $component): void { $index = array_search($component, $this->children, true); if ($index !== false) { unset($this->children[$index]); // Opcional: reindexar el array si es necesario $this->children = array_values($this->children); } } /** * Método clave del patrón Composite: * - Se comporta como hoja (muestra su nombre) * - Pero además renderiza recursivamente a TODOS sus hijos * - El $level + 1 crea la indentación de subniveles */ public function render(int $level = 0): string { $indent = str_repeat(' ', $level); // Mostramos el nombre del grupo con un símbolo distintivo (+) $output = $indent . "+ {$this->name}\n"; // Recorremos y renderizamos cada hijo (recursividad) foreach ($this->children as $child) { $output .= $child->render($level + 1); } return $output; } }
  • Luego ya usamos el inerface para el patrón composite
    $inicio = new MenuItem("Inicio", "/");
    $productos  = new MenuItem("Productos", "/productos");
    $servicios  = new MenuItem("Servicios", "/servicios");
    
    // Creamos un subgrupo "Empresa"
    $empresa = new MenuGroup("Empresa");
    $empresa->add(new MenuItem("Nosotros", "/nosotros"));
    $empresa->add(new MenuItem("Equipo", "/equipo"));
    $empresa->add(new MenuItem("Contacto", "/contacto"));
    
    // Creamos otro subgrupo "Soporte"
    $soporte = new MenuGroup("Soporte");
    $soporte->add(new MenuItem("FAQ", "/faq"));
    $soporte->add(new MenuItem("Soporte técnico", "/soporte"));
    
    // Menú principal (raíz del árbol)
    $menuPrincipal = new MenuGroup("Menú Principal");
    $menuPrincipal->add($inicio);
    $menuPrincipal->add($productos);
    $menuPrincipal->add($servicios);
    $menuPrincipal->add($empresa);
    $menuPrincipal->add($soporte);
    
    // El cliente NO necesita saber si es hoja o compuesto
    echo $menuPrincipal->render();