Composición vs. herencia
Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy. Lección 56 de 91.
En el capítulo anterior, aprendiste que los traits proporcionan una capacidad de «tiene-un», mientras que la herencia establece una relación «es-un». La composición lleva el concepto de «tiene-un» más lejos al construir objetos complejos a partir de otros más simples y almacenarlos como propiedades en lugar de heredar de ellos.
Con la herencia, un Car podría extender Vehicle. Con la composición, un Car contiene un objeto Engine:
<?php
class Engine {
public function start(): string {
return "Engine started";
}
}
class Car {
private Engine $engine;
public function __construct() {
$this->engine = new Engine();
}
public function start(): string {
return $this->engine->start();
}
}
$car = new Car();
echo $car->start();
Salida:
Engine startedLa clase Car no hereda de Engine; posee una. Este enfoque ofrece ventajas significativas: puedes cambiar el motor por una implementación diferente, las clases permanecen débilmente acopladas y evitas las jerarquías rígidas que crea la herencia profunda.
Una pauta común en OOP es «favorecer la composición sobre la herencia». La herencia funciona bien para jerarquías de tipos genuinas (un Dog realmente es un Animal), pero la composición destaca cuando ensamblas funcionalidades a partir de partes independientes. Puedes combinar varios objetos, reemplazarlos en tiempo de ejecución y probar los componentes de forma aislada.
La siguiente lección explora la inyección de dependencias, que se basa directamente en la composición al controlar cómo los objetos reciben sus dependencias.
Desafío
FácilConstruyamos un sistema informático mediante composición, donde un Computer se ensambla a partir de objetos de componentes independientes en lugar de heredar de ellos. Esto demuestra cómo la composición permite construir objetos complejos a partir de partes más sencillas e independientes.
Organizarás tu código en tres archivos:
Processor.php: Crea una claseProcessorque represente una CPU. Usa la promoción de constructor para definir un$modelprivado (cadena) y un$coresprivado (entero). Añade un métodogetSpecs()que devuelva"[model] ([cores] cores)".Memory.php: Crea una claseMemoryque represente la RAM. Usa la promoción de constructor para definir un$sizeprivado (entero que representa los GB). Añade un métodogetCapacity()que devuelva"[size]GB RAM".main.php: Incluye ambos archivos de componentes y crea una claseComputerque use composición.Computerdebe:- Almacenar un objeto
Processory un objetoMemorycomo propiedades privadas - Aceptar un modelo de procesador, un número de núcleos y un tamaño de memoria en su constructor
- Crear internamente los objetos de los componentes durante la construcción
- Tener un método
describe()que devuelva"Computer with [processor specs] and [memory capacity]"
Recibirás tres entradas: un modelo de procesador, un número de núcleos y un tamaño de memoria en GB. Crea un
Computercon estas especificaciones e imprime el resultado de llamar adescribe().- Almacenar un objeto
Observa que Computer no extiende Processor ni Memory. Los contiene. Esta relación de tipo «tiene» mediante la composición mantiene cada componente independiente y reutilizable, y permite que Computer delegue en sus partes cuando sea necesario.
Pruébalo tú mismo
<?php
require_once 'Processor.php';
require_once 'Memory.php';
class Computer
{
// TODO: Define propiedades privadas para almacenar objetos Processor y Memory
// TODO: El constructor debe aceptar el modelo del procesador, la cantidad de núcleos y el tamaño de la memoria
// y crear los objetos de los componentes internamente
public function __construct()
{
// TODO: Crea los objetos Processor y Memory y almacénalos
}
// TODO: Implementa el método describe() que devuelve
// "Computer with [processor specs] and [memory capacity]"
public function describe(): string
{
// TODO: Delega a los objetos componentes y devuelve la cadena formateada
}
}
// Leer la entrada
$processorModel = trim(fgets(STDIN));
$cores = intval(fgets(STDIN));
$memorySize = intval(fgets(STDIN));
// TODO: Crea un Computer con las especificaciones dadas e imprime el resultado de describe()
?>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