Traits frente a herencia
Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 55 de 91.
Ahora que entiendes cómo funcionan los traits, aclaremos cuándo usar traits frente a la herencia. Ambos permiten la reutilización de código, pero sirven para propósitos diferentes y tienen características distintas.
La herencia establece una relación "es-un" (is-a). Una clase Dog que extiende a Animal significa que un perro es un animal. La clase hija hereda la identidad del padre y puede ser utilizada en cualquier lugar donde se espere el tipo padre.
Los traits proporcionan una capacidad "tiene-un" (has-a). Cuando una clase usa un trait Loggable, tiene funcionalidad de registro, pero no se convierte en un registrador. Los traits añaden comportamiento sin cambiar lo que la clase es fundamentalmente.
<?php
// Herencia: Dog ES un Animal
class Animal {
public function breathe(): void {
echo "Breathing";
}
}
class Dog extends Animal {
public function bark(): void {
echo "Woof!";
}
}
// Trait: Dog TIENE capacidad de logging
trait Loggable {
public function log(string $msg): void {
echo "Log: $msg";
}
}
class Cat extends Animal {
use Loggable;
}
Diferencias clave a recordar:
| Aspecto | Herencia | Traits |
|---|---|---|
| Relación | es-un (is-a) | tiene-una capacidad (has-a) |
| Límite | Solo un padre | Se permiten múltiples traits |
| Comprobación de tipos | Funciona con instanceof | Sin identidad de tipo |
| Ideal para | Relaciones jerárquicas | Comportamientos compartidos entre clases no relacionadas |
Usa la herencia cuando las clases compartan una identidad común. Usa traits cuando clases no relacionadas necesiten la misma funcionalidad sin formar parte del mismo árbol genealógico.
Desafío
FácilVamos a construir un sistema de vehículos que demuestre cuándo usar la herencia frente a los traits. Crearás una jerarquía de vehículos que comparten una identidad común a través de la herencia, mientras añades capacidades transversales mediante traits.
Organizarás tu código en cuatro archivos:
Trackable.php— Crea un trait llamadoTrackableque proporcione capacidad de rastreo GPS. El trait debe tener:- Una propiedad privada
$location(string) - Un método
setLocation(string $location)que almacene la ubicación actual - Un método
getLocation()que devuelva"Location: " . $this->location
- Una propiedad privada
Vehicle.php— Crea una clase baseVehicleque establezca la relación "es un" (is-a). Incluye el archivo del trait. Usa la promoción de constructor para definir una propiedad protegida$brand. Añade un métododescribe()que devuelva"Vehicle: " . $this->brand. Esta clase debe usar el traitTrackable, ya que todos los vehículos de nuestra flota tendrán GPS.Car.php— Crea una claseCarque extienda deVehicle. Incluye el archivo de Vehicle. Usa la promoción de constructor para aceptar la marca y una propiedad privada$doors(integer). Llama al constructor padre con la marca. Sobrescribe el métododescribe()para que devuelva"Car: " . $this->brand . " with " . $this->doors . " doors".main.php— Incluye el archivo de Car. Recibirás tres entradas: un nombre de marca, un número de puertas y una cadena de ubicación. Crea una instancia deCar, establece su ubicación y luego imprime dos líneas:- El resultado de llamar a
describe() - El resultado de llamar a
getLocation()
- El resultado de llamar a
Observa cómo la herencia y los traits trabajan juntos aquí: Car es un Vehicle (la herencia establece la identidad), mientras que Vehicle tiene capacidad de rastreo (el trait añade comportamiento). Podrías usar instanceof Vehicle para verificar si algo es un vehículo, pero no hay un tipo "Trackable" contra el cual verificar; esa es la diferencia clave entre estos dos enfoques.
Hoja de referencia
Usa la herencia para relaciones "es-un" y los traits para capacidades "tiene-un".
La herencia establece identidad: una clase hija es un tipo de clase padre y puede usarse en cualquier lugar donde se espere el tipo padre.
Los traits añaden comportamiento sin cambiar la identidad de la clase: una clase tiene funcionalidad pero no se convierte en esa cosa.
<?php
// Herencia: Dog ES un Animal
class Animal {
public function breathe(): void {
echo "Breathing";
}
}
class Dog extends Animal {
public function bark(): void {
echo "Woof!";
}
}
// Trait: Cat TIENE capacidad de registro (logging)
trait Loggable {
public function log(string $msg): void {
echo "Log: $msg";
}
}
class Cat extends Animal {
use Loggable;
}
| Aspecto | Herencia | Traits |
|---|---|---|
| Relación | es-un | capacidad tiene-un |
| Límite | Solo un padre único | Se permiten múltiples traits |
| Comprobación de tipos | Funciona con instanceof | Sin identidad de tipo |
| Ideal para | Relaciones jerárquicas | Comportamientos compartidos entre clases no relacionadas |
Usa la herencia para relaciones jerárquicas donde las clases comparten una identidad común. Usa traits para comportamientos compartidos entre clases no relacionadas.
Pruébalo tú mismo
<?php
// Archivo principal - crear y probar la instancia de Car
// TODO: Incluir el archivo Car.php
// Leer entradas
$brand = trim(fgets(STDIN));
$doors = intval(trim(fgets(STDIN)));
$location = trim(fgets(STDIN));
// TODO: Crear una instancia de Car con marca y puertas
// TODO: Establecer la ubicación del coche
// TODO: Imprimir el resultado de describe()
// TODO: Imprimir el resultado de getLocation()
?>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