Menu
Coddy logo textTech

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.

challenge icon

Desafío

Fácil

Construyamos 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 interfaz Refuelable con 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 abstracta Vehicle que proporcione funcionalidad compartida para todos los vehículos. Debe tener una propiedad protegida $brand y una propiedad protegida $fuelLevel (comenzando en 0). El constructor acepta el nombre de la marca. Incluye un método concreto getBrand() que devuelva la marca, un método concreto getFuelLevel() que devuelva el nivel de combustible actual y un método abstracto drive() que las clases hijas deben implementar.
  • Car.php — Crea una clase Car que extienda de Vehicle e implemente Refuelable. Incluye ambos archivos requeridos en la parte superior. El constructor acepta la marca y la pasa al constructor padre. Implementa drive() para que devuelva "[brand] car is driving". Implementa refuel($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 un Car con la marca. Imprime el resultado de drive() en la primera línea, luego imprime el resultado de refuel() 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
?>
quiz iconPonte a prueba

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