Interfaz vs Clase Abstracta
Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 30 de 91.
Ahora que has aprendido tanto sobre las interfaces como sobre las clases abstractas, aclaremos cuándo usar cada una. Aunque puedan parecer similares, sirven para propósitos diferentes y tienen capacidades distintas.
| Característica | Interfaz | Clase abstracta |
|---|---|---|
| Métodos | Solo firmas de métodos | Tanto métodos abstractos como concretos |
| Propiedades | Solo constantes | Puede tener propiedades |
| Herencia múltiple | Una clase puede implementar muchas | Una clase solo puede extender una |
| Constructor | No permitido | Puede tener constructor |
Usa una interfaz cuando quieras definir un contrato que clases no relacionadas puedan cumplir. Por ejemplo, tanto un Printer como un Logger podrían implementar una interfaz Writable, aunque no compartan ningún ancestro común.
Utiliza una clase abstracta cuando tengas clases relacionadas que compartan código común. Si tus clases Dog y Cat necesitan la misma implementación del método eat(), una clase abstracta Animal te permite escribir ese código una sola vez.
También puedes combinar ambos enfoques. Una clase puede extender una clase abstracta y, al mismo tiempo, implementar múltiples interfaces:
<?php
abstract class Animal {
protected $name;
public function eat() {
return $this->name . " is eating";
}
}
interface Swimmable {
public function swim();
}
class Duck extends Animal implements Swimmable {
public function __construct($name) {
$this->name = $name;
}
public function swim() {
return $this->name . " is swimming";
}
}
Punto clave: Elige interfaces para definir capacidades en clases no relacionadas. Elige clases abstractas cuando necesites compartir código entre clases relacionadas.
Desafío
FácilConstruyamos un sistema de vehículos que demuestre cuándo usar una clase abstracta frente a una interfaz, y cómo combinar ambos enfoques de manera efectiva.
Crearás un sistema donde los vehículos comparten propiedades comunes a través de la herencia, pero también pueden adquirir capacidades adicionales a través de interfaces. Esto refleja las decisiones de diseño del mundo real: un coche "es un" vehículo (herencia), pero también "tiene la capacidad" de ser repostable (interfaz).
Organiza tu código en cuatro archivos:
Refuelable.php— Define una interfazRefuelablecon la firma de un único método:refuel($amount). Esta capacidad puede aplicarse a cualquier clase que necesite repostar, independientemente de su jerarquía de herencia.Vehicle.php— Define una clase abstractaVehicleque proporcione funcionalidad compartida para todos los vehículos. Debe tener una propiedad protegida$brandy una propiedad protegida$fuelLevel(comenzando en 0). El constructor acepta el nombre de la marca. Incluye un método concretogetBrand()que devuelva la marca, un método concretogetFuelLevel()que devuelva el nivel de combustible actual y un método abstractodrive()que las clases hijas deben implementar.Car.php— Crea una claseCarque extienda deVehiclee implementeRefuelable. Incluye ambos archivos requeridos en la parte superior. El constructor acepta la marca y la pasa al constructor padre. Implementadrive()para que devuelva"[brand] car is driving". Implementarefuel($amount)para sumar la cantidad al nivel de combustible y devolver"[brand] refueled to [fuelLevel] liters".main.php— Incluye el archivo Car. Recibirás dos entradas: un nombre de marca y una cantidad de combustible (conviértela a entero). Crea unCarcon la marca. Imprime el resultado dedrive()en la primera línea, luego imprime el resultado derefuel()con la cantidad de combustible en la segunda línea.
Este desafío ilustra la distinción clave: la clase abstracta Vehicle comparte código y establece una relación de tipo "es-un", mientras que la interfaz Refuelable añade una capacidad que podría aplicarse a otras clases no relacionadas (como un generador o un barco) sin requerir que sean vehículos.
Hoja de referencia
Las interfaces y las clases abstractas sirven para propósitos diferentes:
| Característica | Interfaz | Clase Abstracta |
|---|---|---|
| Métodos | Solo firmas de métodos | Tanto métodos abstractos como concretos |
| Propiedades | Solo constantes | Puede tener propiedades |
| Herencia múltiple | Una clase puede implementar muchas | Una clase puede extender solo una |
| Constructor | No permitido | Puede tener constructor |
Usa una interfaz para definir un contrato para clases no relacionadas. Usa una clase abstracta para compartir código común entre clases relacionadas.
Una clase puede extender una clase abstracta mientras implementa múltiples interfaces:
<?php
abstract class Animal {
protected $name;
public function eat() {
return $this->name . " is eating";
}
}
interface Swimmable {
public function swim();
}
class Duck extends Animal implements Swimmable {
public function __construct($name) {
$this->name = $name;
}
public function swim() {
return $this->name . " is swimming";
}
}
Pruébalo tú mismo
<?php
require_once 'Car.php';
// Leer entrada
$brand = trim(fgets(STDIN));
$fuelAmount = intval(trim(fgets(STDIN)));
// TODO: Crear un Car con la marca
// TODO: Imprimir el resultado de drive() en la primera línea
// TODO: Imprimir el resultado de refuel() con la cantidad de combustible en la segunda línea
?>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