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 necesitas que la clase que usa el trait implemente una funcionalidad específica. Aquí es donde resultan útiles los métodos abstractos en los traits: definen un contrato que la clase que los usa debe cumplir.
Cuando un rasgo declara un método abstracto, cualquier clase que use ese rasgo 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!");
Salida:
Sending to john@example.com: Welcome!El trait proporciona el método sendNotification(), pero depende de que la clase implemente getEmail(). Esto crea un patrón potente en el que el trait gestiona 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 produce un error fatal. Esto garantiza que los traits puedan depender de forma segura de la existencia de ciertos métodos, lo que hace que tu código sea más predecible y fácil de mantener.
Desafío
FácilConstruyamos un sistema de precios en el que los distintos tipos de productos calculen sus precios finales de manera diferente, pero todos compartan la lógica común de descuentos mediante 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 descuentos. El trait debe tener:- Un método abstracto
getBasePrice(): floatque las clases que utilicen este trait deban implementar - Un método
applyDiscount(int $percent)que calcule y devuelva el precio después de aplicar el porcentaje de descuento al precio base - Un método
getFinalPrice(int $percent)que devuelva una cadena con formato:"Final price: $[discounted_price]"con 2 decimales
- Un método abstracto
Product.php: crea una claseProductque utilice el traitDiscountable. Incluye el archivo del trait. Usa la promoción del 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 con el formato
"Base: $[price]"y 2 decimales - El resultado de llamar a
getFinalPrice()con el porcentaje de descuento
- El precio base con el formato
Este patrón es potente porque el trait Discountable puede ser utilizado por cualquier clase —productos, servicios o suscripciones— siempre que implementen getBasePrice(). El trait proporciona la lógica compartida de descuentos, mientras cada clase define de dónde proviene su precio base.
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 name y el price
// 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 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