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 "tiene-un" (has-a) mientras que la herencia establece una relación "es-un" (is-a). La composición lleva el concepto "tiene-un" más allá al construir objetos complejos a partir de otros más simples, almacenándolos 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();
Resultado:
Engine startedLa clase Car no hereda de Engine; posee uno. Este enfoque ofrece ventajas significativas: puedes intercambiar 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 la POO es "favorecer la composición sobre la herencia." La herencia funciona bien para jerarquías de tipos genuinas (un Dog es verdaderamente un Animal), pero la composición brilla cuando estás ensamblando funcionalidad a partir de partes independientes. Puedes combinar múltiples objetos, reemplazarlos en tiempo de ejecución y probar 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ácilVamos a construir un sistema informático utilizando composición, donde una clase Computer se ensambla a partir de objetos componentes separados en lugar de heredar de ellos. Esto demuestra cómo la composición permite construir objetos complejos a partir de partes más simples 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$model(string) privado y un$cores(integer) privado. Añade un métodogetSpecs()que devuelva"{$this->model} ({$this->cores} cores)".Memory.php— Crea una claseMemoryque represente la RAM. Usa la promoción de constructor para definir un$size(integer que representa GB) privado. Añade un métodogetCapacity()que devuelva"{$this->size}GB RAM".main.php— Incluye ambos archivos de componentes y crea una claseComputerque utilice composición. La claseComputerdebe:- 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 los objetos componentes internamente durante la construcción
- Tener un método
describe()que devuelva"Computer with " . $this->processor->getSpecs() . " and " . $this->memory->getCapacity()
Recibirás tres entradas: un modelo de procesador, un número de núcleos y un tamaño de memoria en GB. Crea un objeto
Computercon estas especificaciones e imprime el resultado de llamar adescribe().- Almacenar un objeto
Observa cómo Computer no extiende de Processor ni de Memory; en su lugar, los contiene. Esta relación de "tiene-un" (has-a) a través de la composición mantiene cada componente independiente y reutilizable, permitiendo que Computer delegue tareas a sus partes cuando sea necesario.
Hoja de referencia
La composición construye objetos complejos a partir de otros más simples almacenándolos como propiedades en lugar de heredar de ellos. Esto crea una relación de tipo "tiene un" (has-a) donde una clase contiene instancias de otras clases.
Ejemplo básico de composición:
<?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();
}
}La clase Car no hereda de Engine; posee uno. Esto mantiene las clases débilmente acopladas y evita jerarquías de herencia rígidas.
Prioriza la composición sobre la herencia: La herencia funciona bien para jerarquías de tipos genuinas (un Dog es un Animal), pero la composición destaca al ensamblar funcionalidades a partir de partes independientes. Puedes combinar múltiples objetos, reemplazarlos en tiempo de ejecución y probar componentes de forma aislada.
Pruébalo tú mismo
<?php
require_once 'Processor.php';
require_once 'Memory.php';
class Computer
{
// TODO: Definir propiedades privadas para almacenar los objetos Processor y Memory
// TODO: El constructor debe aceptar el modelo del procesador, el número de núcleos y el tamaño de la memoria
// y crear los objetos de los componentes internamente
public function __construct()
{
// TODO: Crear objetos Processor y Memory y almacenarlos
}
// TODO: Implementar el método describe() que devuelva
// "Computer with [processor specs] and [memory capacity]"
public function describe(): string
{
// TODO: Delegar en los objetos componentes y devolver la cadena formateada
}
}
// Leer entrada
$processorModel = trim(fgets(STDIN));
$cores = intval(fgets(STDIN));
$memorySize = intval(fgets(STDIN));
// TODO: Crear un Computer con las especificaciones dadas e imprimir 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 OOP
Archivos externosIntroducción a OOPClases vs ObjetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Resumen - Calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasResumen - Jerarquía de empleados7Encapsulamiento
Público, Protegido, PrivadoModificadores de acceso a fondoGetters y SettersOcultamiento de informaciónPromoción de constructor (8.0)Resumen - Sistema de registros de estudiantes10Conceptos avanzados de POO
Composición vs. HerenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibers (PHP 8.1)Clonación de objetos a fondoGeneradores e Iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Espacios de nombres y Autoloading
Introducción a los espacios de nombresLa palabra clave useEstándar de Autoloading PSR-4Autoloader de ComposerResumen - Proyecto organizado5Interfaces y Contratos
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesInterfaz vs Clase AbstractaType Hinting con InterfacesResumen - 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 __unserializeResumen - 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)Resumen - Gestor de cuenta bancaria6Polimorfismo
Sobrescritura de métodos revisadaPolimorfismo mediante interfacesType Hinting y tipos de uniónEnlace estático tardíoResumen - Procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos de TraitsMétodos abstractos en TraitsTraits frente a herencia12Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy