Métodos abstractos en Traits
Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 54 de 91.
Los traits pueden proporcionar métodos listos para usar, pero a veces se necesita que la clase que utiliza el trait implemente una funcionalidad específica. Aquí es donde los métodos abstractos en los traits resultan útiles: definen un contrato que la clase que los utiliza debe cumplir.
Cuando un trait declara un método abstracto, cualquier clase que use ese trait debe implementarlo:
<?php
trait Notifiable {
abstract public function getEmail(): string;
public function sendNotification(string $message): void {
echo "Sending to " . $this->getEmail() . ": $message";
}
}
class User {
use Notifiable;
public function __construct(private string $email) {}
public function getEmail(): string {
return $this->email;
}
}
$user = new User("john@example.com");
$user->sendNotification("Welcome!");
Resultado:
Sending to john@example.com: Welcome!El trait proporciona el método sendNotification() pero depende de que getEmail() sea implementado por la clase. Esto crea un patrón potente donde el trait maneja la lógica común mientras delega los detalles específicos de la clase a la clase que lo implementa.
Si una clase utiliza el trait sin implementar el método abstracto, PHP lanza un error fatal. Esto garantiza que los traits puedan depender de forma segura de la existencia de ciertos métodos, haciendo que su código sea más predecible y mantenible.
Desafío
FácilVamos a construir un sistema de precios donde diferentes tipos de productos calculan sus precios finales de manera distinta, pero todos comparten una lógica de descuento común a través de un trait con un método abstracto.
Organizarás tu código en tres archivos:
Discountable.php— Crea un trait llamadoDiscountableque proporcione la funcionalidad de descuento. El trait debe tener:- Un método abstracto
getBasePrice(): floatque las clases que usen este trait deben implementar - Un método
applyDiscount(int $percent)que calcula y devuelve el precio después de aplicar el porcentaje de descuento al precio base - Un método
getFinalPrice(int $percent)que devuelve una cadena formateada:"Final price: $[discounted_price]"con 2 decimales
- Un método abstracto
Product.php— Crea una claseProductque use el traitDiscountable. Incluye el archivo del trait. Usa la promoción de constructor para definir un$name(string) privado y un$price(float) privado. Implementa el método requeridogetBasePrice()para devolver el precio del producto.main.php— Incluye el archivo Product. Recibirás tres entradas: un nombre de producto, un precio y un porcentaje de descuento. Crea una instancia deProducte imprime dos líneas:- El precio base formateado como
"Base: $[price]"con 2 decimales - El resultado de llamar a
getFinalPrice()con el porcentaje de descuento
- El precio base formateado como
Este patrón es potente porque el trait Discountable puede ser utilizado por cualquier clase — productos, servicios, suscripciones — siempre que implementen getBasePrice(). El trait proporciona la lógica de descuento compartida mientras que cada clase define de dónde proviene su precio base.
Hoja de referencia
Los traits pueden declarar métodos abstractos que deben ser implementados por cualquier clase que use el trait:
<?php
trait Notifiable {
abstract public function getEmail(): string;
public function sendNotification(string $message): void {
echo "Sending to " . $this->getEmail() . ": $message";
}
}
class User {
use Notifiable;
public function __construct(private string $email) {}
public function getEmail(): string {
return $this->email;
}
}
El trait proporciona métodos listos para usar mientras requiere que la clase implemente una funcionalidad específica. Si una clase usa el trait sin implementar el método abstracto, PHP lanza un error fatal.
Este patrón permite que los traits manejen la lógica común mientras delegan los detalles específicos de la clase a la clase que los implementa.
Pruébalo tú mismo
<?php
require_once 'Product.php';
// Leer entradas
$name = trim(fgets(STDIN));
$price = floatval(trim(fgets(STDIN)));
$discountPercent = intval(trim(fgets(STDIN)));
// TODO: Crear una instancia de Product con el nombre y el precio
// TODO: Imprimir el precio base formateado como "Base: $[price]" con 2 decimales
// TODO: Imprimir el resultado de llamar a getFinalPrice() con el porcentaje de descuento
?>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