Menu
Coddy logo textTech

Interfaces 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 las interfaces como las clases abstractas, aclaremos cuándo usar cada una. Aunque puedan parecer similares, tienen propósitos diferentes y capacidades distintas.

Característica Interfaz Clase abstracta
Métodos Solo firmas de métodos Métodos abstractos y concretos
Propiedades Solo constantes Puede tener propiedades
Herencia múltiple Una clase puede implementar muchas Una clase solo puede extender una
Constructor Puede declarar una firma de __construct (sin cuerpo) Puede definir un constructor con un cuerpo

Usa una interfaz cuando quieras definir un contrato que puedan cumplir clases no relacionadas. Por ejemplo, tanto una Printer como un Logger podrían implementar una interfaz Writable, aunque no compartan ningún antecesor común.

Usa una clase abstracta cuando tengas clases relacionadas que compartan código común. Si tus clases Dog y Cat necesitan ambas 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 mientras implementa varias 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 entre 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 en lugar de una interfaz, y cómo combinar ambos enfoques de manera eficaz.

Crearás un sistema en el que los vehículos compartan propiedades comunes mediante la herencia, pero también puedan adquirir capacidades adicionales mediante interfaces. Esto refleja decisiones de diseño del mundo real: un automóvil «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 una única signatura de 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 (con un valor inicial de 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 Vehicle e implemente Refuelable. Incluye ambos archivos requeridos al principio. El constructor acepta la marca y se la pasa al constructor padre. Implementa drive() para devolver "[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 de Car. Recibirás dos entradas: un nombre de marca y una cantidad de combustible (conviértela a un entero). Crea un Car con la marca. Imprime el resultado de drive() en la primera línea y, después, imprime el resultado de refuel() con la cantidad de combustible en la segunda línea.

Este desafío ilustra la diferencia clave: la clase abstracta Vehicle comparte código y establece una relación de «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 exigirles que sean vehículos.

Pruébalo tú mismo

<?php
require_once 'Car.php';

// Leer entrada
$brand = trim(fgets(STDIN));
$fuelAmount = intval(trim(fgets(STDIN)));

// TODO: Crea un Car con la marca

// TODO: Imprime el resultado de drive() en la primera línea

// TODO: Imprime 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

Practica por tu cuenta: Compilador de PHP online