Las interfaces y las clases abstractas son los dos mecanismos que PHP ofrece para definir contratos entre componentes. Elegir el incorrecto complica el diseño: las interfaces definen qué hace algo, las clases abstractas comparten cómo lo hace parcialmente. Este artículo muestra la diferencia con ejemplos reales.
Interfaces: definir un contrato puro
Una interfaz declara métodos sin implementación. Cualquier clase que la implemente con implements está obligada a definir todos esos métodos:
<?php
interface Pagable
{
public function cobrar(float $cantidad): bool;
public function reembolsar(float $cantidad): bool;
public function getEstado(): string;
}
class PagoTarjeta implements Pagable
{
private string $estado = 'pendiente';
public function cobrar(float $cantidad): bool
{
// lógica real contra pasarela de pago
$this->estado = 'completado';
return true;
}
public function reembolsar(float $cantidad): bool
{
$this->estado = 'reembolsado';
return true;
}
public function getEstado(): string
{
return $this->estado;
}
}
class PagoPayPal implements Pagable
{
private string $estado = 'pendiente';
public function cobrar(float $cantidad): bool
{
$this->estado = 'completado';
return true;
}
public function reembolsar(float $cantidad): bool
{
$this->estado = 'reembolsado';
return true;
}
public function getEstado(): string
{
return $this->estado;
}
}
// El código que usa la interfaz no sabe qué implementación tiene
function procesarPedido(Pagable $pago, float $total): void
{
if ($pago->cobrar($total)) {
echo "Pago de {$total} completado. Estado: " . $pago->getEstado();
}
}
procesarPedido(new PagoTarjeta(), 99.90);
// Pago de 99.9 completado. Estado: completado
?>
Clases abstractas: compartir lógica parcial
Una clase abstracta puede tener métodos implementados y también métodos abstractos que las subclases deben implementar. No se puede instanciar directamente:
<?php
abstract class Reporte
{
// Método con implementación compartida
public function generar(): string
{
$cabecera = $this->getCabecera();
$cuerpo = $this->getCuerpo();
$pie = $this->getPie();
return $cabecera . "n" . $cuerpo . "n" . $pie;
}
protected function getCabecera(): string
{
return '=== ' . $this->getTitulo() . ' ===';
}
protected function getPie(): string
{
return 'Generado el ' . date('d/m/Y');
}
// Métodos que cada subclase debe implementar
abstract protected function getTitulo(): string;
abstract protected function getCuerpo(): string;
}
class ReporteVentas extends Reporte
{
protected function getTitulo(): string { return 'Ventas del mes'; }
protected function getCuerpo(): string { return 'Total vendido: 15.000 '; }
}
class ReporteUsuarios extends Reporte
{
protected function getTitulo(): string { return 'Usuarios activos'; }
protected function getCuerpo(): string { return 'Usuarios este mes: 342'; }
}
echo (new ReporteVentas())->generar();
// === Ventas del mes ===
// Total vendido: 15.000
// Generado el 26/06/2026
?>
¿Cuándo usar cada una?
Criterio | Interfaz | Clase abstracta |
¿Comparte implementación? | No | Sí |
¿Herencia múltiple? | Sí (una clase puede implementar varias) | No (solo una clase padre) |
¿Relación "es un"? | Capacidad / contrato | Especialización real |
¿Instanciable? | No | No |
El error más común: elegir abstract cuando necesitas interface
<?php
// MAL: usas abstract pero no hay lógica compartida
abstract class NotificadorAbstracto
{
abstract public function enviar(string $mensaje): bool;
}
// BIEN: es un contrato puro, usa interface
interface Notificador
{
public function enviar(string $mensaje): bool;
}
class NotificadorEmail implements Notificador
{
public function enviar(string $mensaje): bool
{
// mail($destinatario, 'Notificación', $mensaje);
return true;
}
}
class NotificadorSMS implements Notificador
{
public function enviar(string $mensaje): bool
{
// API SMS
return true;
}
}
// Una clase puede implementar ambas si tiene sentido
interface Registrable
{
public function registrarLog(string $evento): void;
}
class NotificadorAuditado implements Notificador, Registrable
{
public function enviar(string $mensaje): bool { return true; }
public function registrarLog(string $evento): void { /* ... */ }
}
?>
La documentación oficial de interfaces en PHP y la de clases abstractas describen las restricciones de cada mecanismo y cómo se combinan con tipos e interfaces extendidas.
