Leccion 33 de 75 10 min de lectura

Interfaces

Una interface es un contrato que define que metodos debe tener una clase, sin especificar como implementarlos. A diferencia de las clases, puedes implementar multiples interfaces.

¿Que es una interface?

Una interface define firmas de metodos que las clases deben implementar. No contiene logica, solo la estructura:

PHP
<?php
declare(strict_types=1);

interface Imprimible
{
    public function imprimir(): string;
}

class Documento implements Imprimible
{
    private string $contenido;

    public function __construct(string $contenido)
    {
        $this->contenido = $contenido;
    }

    public function imprimir(): string
    {
        return "Documento: {$this->contenido}";
    }
}

class Factura implements Imprimible
{
    private float $total;

    public function __construct(float $total)
    {
        $this->total = $total;
    }

    public function imprimir(): string
    {
        return "Factura: \${$this->total}";
    }
}

$doc = new Documento('Contrato de servicios');
$factura = new Factura(199.99);

echo $doc->imprimir();     // Documento: Contrato de servicios
echo $factura->imprimir(); // Factura: $199.99

Implementar multiples interfaces

Una clase puede implementar varias interfaces, superando la limitacion de la herencia simple:

PHP
<?php
declare(strict_types=1);

interface Guardable
{
    public function guardar(): bool;
}

interface Exportable
{
    public function exportar(string $formato): string;
}

class Reporte implements Guardable, Exportable
{
    private string $titulo;
    private string $contenido;

    public function __construct(string $titulo, string $contenido)
    {
        $this->titulo = $titulo;
        $this->contenido = $contenido;
    }

    public function guardar(): bool
    {
        // Logica para guardar en base de datos
        return true;
    }

    public function exportar(string $formato): string
    {
        return match ($formato) {
            'json' => json_encode(['titulo' => $this->titulo, 'contenido' => $this->contenido]),
            'csv' => "{$this->titulo},{$this->contenido}",
            default => $this->contenido,
        };
    }
}

$reporte = new Reporte('Ventas Q1', 'Total: $50,000');
$reporte->guardar();
echo $reporte->exportar('json');
Convencion de nombres

Los nombres de interfaces suelen ser adjetivos (Imprimible, Guardable) o sustantivos con sufijo "able/ible" que describen una capacidad.

Type hinting con interfaces

Las interfaces permiten escribir codigo flexible que trabaja con cualquier objeto que cumpla el contrato:

PHP
<?php
declare(strict_types=1);

interface Logger
{
    public function log(string $mensaje): void;
}

class FileLogger implements Logger
{
    public function log(string $mensaje): void
    {
        echo "[Archivo] $mensaje\n";
    }
}

class DatabaseLogger implements Logger
{
    public function log(string $mensaje): void
    {
        echo "[Base de datos] $mensaje\n";
    }
}

class Aplicacion
{
    private Logger $logger;

    public function __construct(Logger $logger)
    {
        $this->logger = $logger;  // Acepta cualquier Logger
    }

    public function ejecutar(): void
    {
        $this->logger->log('Aplicacion iniciada');
    }
}

$app1 = new Aplicacion(new FileLogger());
$app1->ejecutar(); // [Archivo] Aplicacion iniciada

$app2 = new Aplicacion(new DatabaseLogger());
$app2->ejecutar(); // [Base de datos] Aplicacion iniciada

Interface vs Clase abstracta

Caracteristica Interface Clase abstracta
Metodos Solo firmas Abstractos y concretos
Propiedades Solo constantes Cualquier tipo
Herencia/Implementacion Multiple Simple
Uso Definir capacidades Compartir codigo comun

Ejemplo practico: Sistema de notificaciones

PHP
<?php
declare(strict_types=1);

interface Notificador
{
    public function enviar(string $destinatario, string $mensaje): bool;
}

class EmailNotificador implements Notificador
{
    public function enviar(string $destinatario, string $mensaje): bool
    {
        echo "Email a $destinatario: $mensaje\n";
        return true;
    }
}

class SMSNotificador implements Notificador
{
    public function enviar(string $destinatario, string $mensaje): bool
    {
        echo "SMS a $destinatario: $mensaje\n";
        return true;
    }
}

class ServicioNotificaciones
{
    /** @var Notificador[] */
    private array $notificadores = [];

    public function agregar(Notificador $notificador): void
    {
        $this->notificadores[] = $notificador;
    }

    public function notificarTodos(string $destinatario, string $mensaje): void
    {
        foreach ($this->notificadores as $notificador) {
            $notificador->enviar($destinatario, $mensaje);
        }
    }
}

$servicio = new ServicioNotificaciones();
$servicio->agregar(new EmailNotificador());
$servicio->agregar(new SMSNotificador());

$servicio->notificarTodos('usuario@email.com', 'Tu pedido ha sido enviado');
// Email a usuario@email.com: Tu pedido ha sido enviado
// SMS a usuario@email.com: Tu pedido ha sido enviado

Ejercicios

Ejercicio 1: Reproductor multimedia

Crea una interface Reproducible con metodos reproducir(), pausar() y detener(). Implementa clases Cancion y Video.

Ver solucion
PHP
<?php
declare(strict_types=1);

interface Reproducible
{
    public function reproducir(): string;
    public function pausar(): string;
    public function detener(): string;
}

class Cancion implements Reproducible
{
    private string $titulo;
    private string $artista;

    public function __construct(string $titulo, string $artista)
    {
        $this->titulo = $titulo;
        $this->artista = $artista;
    }

    public function reproducir(): string
    {
        return "Reproduciendo cancion: {$this->titulo} - {$this->artista}";
    }

    public function pausar(): string
    {
        return "Cancion pausada: {$this->titulo}";
    }

    public function detener(): string
    {
        return "Cancion detenida: {$this->titulo}";
    }
}

class Video implements Reproducible
{
    private string $titulo;
    private int $duracion;

    public function __construct(string $titulo, int $duracion)
    {
        $this->titulo = $titulo;
        $this->duracion = $duracion;
    }

    public function reproducir(): string
    {
        return "Reproduciendo video: {$this->titulo} ({$this->duracion}s)";
    }

    public function pausar(): string
    {
        return "Video pausado: {$this->titulo}";
    }

    public function detener(): string
    {
        return "Video detenido: {$this->titulo}";
    }
}

$cancion = new Cancion('Bohemian Rhapsody', 'Queen');
$video = new Video('Tutorial PHP', 600);

echo $cancion->reproducir(); // Reproduciendo cancion: Bohemian Rhapsody - Queen
echo $video->reproducir();   // Reproduciendo video: Tutorial PHP (600s)

Ejercicio 2: Carrito de compras

Crea Vendible con getPrecio(): float y getNombre(): string. Implementa Producto y Servicio. Crea un carrito que acepte cualquier Vendible.

Ver solucion
PHP
<?php
declare(strict_types=1);

interface Vendible
{
    public function getPrecio(): float;
    public function getNombre(): string;
}

class Producto implements Vendible
{
    private string $nombre;
    private float $precio;

    public function __construct(string $nombre, float $precio)
    {
        $this->nombre = $nombre;
        $this->precio = $precio;
    }

    public function getPrecio(): float
    {
        return $this->precio;
    }

    public function getNombre(): string
    {
        return $this->nombre;
    }
}

class Servicio implements Vendible
{
    private string $nombre;
    private float $tarifaHora;
    private int $horas;

    public function __construct(string $nombre, float $tarifaHora, int $horas)
    {
        $this->nombre = $nombre;
        $this->tarifaHora = $tarifaHora;
        $this->horas = $horas;
    }

    public function getPrecio(): float
    {
        return $this->tarifaHora * $this->horas;
    }

    public function getNombre(): string
    {
        return $this->nombre;
    }
}

class Carrito
{
    /** @var Vendible[] */
    private array $items = [];

    public function agregar(Vendible $item): void
    {
        $this->items[] = $item;
    }

    public function getTotal(): float
    {
        $total = 0;
        foreach ($this->items as $item) {
            $total += $item->getPrecio();
        }
        return $total;
    }
}

$carrito = new Carrito();
$carrito->agregar(new Producto('Laptop', 999.99));
$carrito->agregar(new Servicio('Consultoria', 50, 3));

echo $carrito->getTotal(); // 1149.99

Ejercicio 3: Autenticacion

Crea Autenticable con login(string $password): bool y logout(): void. Implementa UsuarioNormal y UsuarioAdmin (el admin requiere codigo 2FA adicional).

Ver solucion
PHP
<?php
declare(strict_types=1);

interface Autenticable
{
    public function login(string $password): bool;
    public function logout(): void;
}

class UsuarioNormal implements Autenticable
{
    private string $email;
    private string $passwordHash;
    private bool $logueado = false;

    public function __construct(string $email, string $password)
    {
        $this->email = $email;
        $this->passwordHash = password_hash($password, PASSWORD_DEFAULT);
    }

    public function login(string $password): bool
    {
        if (password_verify($password, $this->passwordHash)) {
            $this->logueado = true;
            return true;
        }
        return false;
    }

    public function logout(): void
    {
        $this->logueado = false;
        echo "Usuario {$this->email} deslogueado\n";
    }
}

class UsuarioAdmin implements Autenticable
{
    private string $email;
    private string $passwordHash;
    private string $codigo2FA;
    private bool $logueado = false;

    public function __construct(string $email, string $password, string $codigo2FA)
    {
        $this->email = $email;
        $this->passwordHash = password_hash($password, PASSWORD_DEFAULT);
        $this->codigo2FA = $codigo2FA;
    }

    public function login(string $password): bool
    {
        return false; // Admin requiere 2FA
    }

    public function loginCon2FA(string $password, string $codigo): bool
    {
        if (password_verify($password, $this->passwordHash) && $codigo === $this->codigo2FA) {
            $this->logueado = true;
            return true;
        }
        return false;
    }

    public function logout(): void
    {
        $this->logueado = false;
        echo "Admin {$this->email} deslogueado\n";
    }
}

$usuario = new UsuarioNormal('user@email.com', 'password123');
$admin = new UsuarioAdmin('admin@email.com', 'admin123', '123456');

$usuario->login('password123'); // true
$admin->loginCon2FA('admin123', '123456'); // true

¿Te está gustando el curso?

Tenemos cursos premium con proyectos reales y soporte personalizado.

Descubrir cursos premium