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 te permite componer objetos en estructuras de árbol y después trabajar con estas estructuras como si fueran objetos individuales. Es perfecto para representar jerarquías de partes y conjuntos en las que quieres tratar de manera uniforme los objetos individuales y los grupos de objetos.
Considera 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 los archivos como en las carpetas, y las carpetas deben sumar recursivamente su contenido. El patrón Composite hace que esto sea sencillo al proporcionar a las hojas (archivos) y 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();
Salida:
1810El código del cliente llama a getSize() sin preocuparse por si está trabajando con un solo archivo o con una estructura de carpetas profundamente anidada. El patrón gestiona la recursión internamente, lo que facilita trabajar con jerarquías complejas.
Desafío
FácilConstruyamos un sistema de organigrama usando el Patrón Composite. Las empresas tienen una estructura jerárquica en la que los departamentos pueden contener empleados y otros subdepartamentos: una estructura de árbol perfecta para calcular los totales de manera uniforme en toda la organización.
Organizarás tu código en cuatro archivos:
OrganizationUnit.php: Define una interfazOrganizationUnitque implementarán tanto los empleados individuales como los departamentos. Debe tener dos métodos:getName(): stringygetSalary(): int. Esta interfaz común permite tratar de la misma manera a los empleados individuales y a los departamentos completos al calcular los costos.Employee.php: Incluye la interfaz OrganizationUnit y crea una claseEmployeeque la implemente. Un empleado es un nodo hoja en tu árbol organizativo. 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 claseDepartmentque la implemente. Un departamento es un compuesto que puede contener tanto empleados como otros departamentos. Tu departamento debe:- Aceptar un nombre en su constructor
- Implementar
getName()para devolver el nombre del departamento - Tener un método
add(OrganizationUnit $unit): voidpara añadir empleados o subdepartamentos - Implementar
getSalary()para devolver el salario total de todas las unidades que contiene (sumando recursivamente todo lo que haya 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 arreglo de empleados con sus nombres y salarios.
Crea un
Departmentraíz llamado"Company". Para cada departamento del JSON, crea unDepartment, añade sus empleados y luego añade ese departamento a Company. Finalmente, imprime el salario total de la empresa con este formato:Total Salary: [amount]
La ventaja 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 cliente no necesita conocer la estructura interna.
Pruébalo tú mismo
<?php
require_once 'Employee.php';
require_once 'Department.php';
// Lee la entrada JSON
$input = trim(fgets(STDIN));
$data = (array)json_decode($input, true);
// TODO: Crea un Department raíz llamado "Company"
// TODO: Recorre cada department en los datos JSON
// Para cada department:
// - Crea un nuevo objeto Department
// - Añade cada employee a ese department
// - Añade el department a la Company
// TODO: Imprime el salario total en el formato: Total Salary: [amount]
?>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
1Fundamentos de la POO
Archivos externosIntroducción a la POOClases frente a objetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Repaso: calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasRepaso - Jerarquía de empleados7Encapsulación
Público, protegido, privadoModificadores de acceso en profundidadGetters y settersOcultación de la informaciónPromoción del constructor (8.0)Repaso: sistema de registros de estudiantes10Conceptos avanzados de OOP
Composición vs. herenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibras (PHP 8.1)Análisis profundo de la clonación de objetosGeneradores e iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Namespaces y carga automática
Introducción a los NamespacesLa palabra clave useEstándar de carga automática PSR-4Autoloader de ComposerRepaso: proyecto organizado5Interfaces y contratos
Introducción a las interfacesImplementación de interfacesImplementación de múltiples interfacesInterfaces vs. clase abstractaTipado con interfacesRepaso - Calculadora de formas8Métodos mágicos
Introducción a los métodos mágicos__toString y __debugInfo__get, __set, __isset, __unset__call y __callStatic__clone y clonación de objetos__serialize y __unserializeRepaso: colección personalizada3Propiedades de clase
Propiedades de instancia vs. estáticasConstantes en clasesMétodos y propiedades estáticosPropiedades privadas y protegidasPropiedades de solo lectura (PHP 8.1)Repaso: gestor de cuentas bancarias6Polimorfismo
Repaso de la sobrescritura de métodosPolimorfismo mediante interfacesAnotaciones de tipo y tipos uniónEnlace estático tardíoRepaso: procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos entre TraitsMétodos abstractos en TraitsTraits frente a la herencia12Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón StrategyPractica por tu cuenta: Compilador de PHP online