Leccion 32 de 75 10 min de lectura

Clases Abstractas

Las clases abstractas son plantillas incompletas que definen metodos que las clases hijas deben implementar. No se pueden instanciar directamente.

¿Que es una clase abstracta?

Una clase abstracta define una estructura comun pero deja algunos metodos sin implementar. Estos metodos abstractos deben ser implementados por las clases hijas.

PHP
<?php
declare(strict_types=1);

abstract class Forma
{
    protected string $color;

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

    // Metodo abstracto: sin implementacion, solo firma
    abstract public function getArea(): float;

    // Metodo concreto: con implementacion
    public function getColor(): string
    {
        return $this->color;
    }
}

// $forma = new Forma('rojo'); // Error: no se puede instanciar clase abstracta

Implementar métodos abstractos

Las clases hijas deben implementar todos los metodos abstractos, o ser abstractas tambien:

PHP
<?php
declare(strict_types=1);

abstract class Forma
{
    protected string $color;

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

    abstract public function getArea(): float;
    abstract public function getPerimetro(): float;
}

class Rectangulo extends Forma
{
    private float $ancho;
    private float $alto;

    public function __construct(float $ancho, float $alto, string $color)
    {
        parent::__construct($color);
        $this->ancho = $ancho;
        $this->alto = $alto;
    }

    public function getArea(): float
    {
        return $this->ancho * $this->alto;
    }

    public function getPerimetro(): float
    {
        return 2 * ($this->ancho + $this->alto);
    }
}

class Circulo extends Forma
{
    private float $radio;

    public function __construct(float $radio, string $color)
    {
        parent::__construct($color);
        $this->radio = $radio;
    }

    public function getArea(): float
    {
        return M_PI * $this->radio ** 2;
    }

    public function getPerimetro(): float
    {
        return 2 * M_PI * $this->radio;
    }
}

$rectangulo = new Rectangulo(10, 5, 'azul');
$circulo = new Circulo(3, 'rojo');

echo $rectangulo->getArea();     // 50
echo $circulo->getArea();        // 28.27...
echo $rectangulo->getColor();    // azul (metodo heredado)
Abstracto vs Concreto

Una clase abstracta puede tener metodos abstractos (sin cuerpo) y metodos concretos (con implementacion). Las clases hijas heredan los concretos y deben implementar los abstractos.

Ejemplo practico: Sistema de pagos

PHP
<?php
declare(strict_types=1);

abstract class MetodoPago
{
    protected float $cantidad;

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

    abstract public function procesar(): bool;
    abstract public function getNombre(): string;

    public function getCantidad(): float
    {
        return $this->cantidad;
    }

    public function getResumen(): string
    {
        return "Pago de \${$this->cantidad} via {$this->getNombre()}";
    }
}

class PagoTarjeta extends MetodoPago
{
    private string $numeroTarjeta;

    public function __construct(float $cantidad, string $numeroTarjeta)
    {
        parent::__construct($cantidad);
        $this->numeroTarjeta = $numeroTarjeta;
    }

    public function procesar(): bool
    {
        // Logica de procesamiento de tarjeta
        return strlen($this->numeroTarjeta) === 16;
    }

    public function getNombre(): string
    {
        return 'Tarjeta';
    }
}

class PagoPayPal extends MetodoPago
{
    private string $email;

    public function __construct(float $cantidad, string $email)
    {
        parent::__construct($cantidad);
        $this->email = $email;
    }

    public function procesar(): bool
    {
        // Logica de procesamiento PayPal
        return filter_var($this->email, FILTER_VALIDATE_EMAIL) !== false;
    }

    public function getNombre(): string
    {
        return 'PayPal';
    }
}

$tarjeta = new PagoTarjeta(99.99, '1234567890123456');
$paypal = new PagoPayPal(49.99, 'usuario@email.com');

echo $tarjeta->getResumen(); // Pago de $99.99 via Tarjeta
echo $paypal->getResumen();  // Pago de $49.99 via PayPal

if ($tarjeta->procesar()) {
    echo 'Pago con tarjeta procesado';
}

¿Cuando usar clases abstractas?

  • Cuando quieres definir una plantilla que otras clases deben seguir
  • Cuando tienes codigo comun que compartir entre clases relacionadas
  • Cuando algunas operaciones dependen del tipo especifico de clase hija
PHP
<?php
declare(strict_types=1);

abstract class Animal
{
    protected string $nombre;

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

    abstract public function hacerSonido(): string;

    public function presentarse(): string
    {
        return "Soy {$this->nombre} y hago: {$this->hacerSonido()}";
    }
}

class Perro extends Animal
{
    public function hacerSonido(): string
    {
        return 'Guau!';
    }
}

class Gato extends Animal
{
    public function hacerSonido(): string
    {
        return 'Miau!';
    }
}

$perro = new Perro('Rex');
$gato = new Gato('Michi');

echo $perro->presentarse(); // Soy Rex y hago: Guau!
echo $gato->presentarse();  // Soy Michi y hago: Miau!

Ejercicios

Ejercicio 1: Empleados

Crea una clase abstracta Empleado con nombre, salarioBase y metodo abstracto calcularSalario(): float. Implementa EmpleadoFijo (salario = base) y EmpleadoPorHoras (salario = horas * tarifa).

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

abstract class Empleado
{
    protected string $nombre;
    protected float $salarioBase;

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

    abstract public function calcularSalario(): float;

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

class EmpleadoFijo extends Empleado
{
    public function calcularSalario(): float
    {
        return $this->salarioBase;
    }
}

class EmpleadoPorHoras extends Empleado
{
    private int $horasTrabajadas;

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

    public function calcularSalario(): float
    {
        return $this->salarioBase * $this->horasTrabajadas;
    }
}

$fijo = new EmpleadoFijo('Ana', 2500);
$porHoras = new EmpleadoPorHoras('Luis', 15, 160);

echo $fijo->calcularSalario();     // 2500
echo $porHoras->calcularSalario(); // 2400

Ejercicio 2: Notificaciones

Crea Notificacion abstracta con mensaje y metodo abstracto enviar(): bool. Implementa NotificacionEmail y NotificacionSMS con sus propios atributos (email, telefono).

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

abstract class Notificacion
{
    protected string $mensaje;

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

    abstract public function enviar(): bool;

    public function getMensaje(): string
    {
        return $this->mensaje;
    }
}

class NotificacionEmail extends Notificacion
{
    private string $email;

    public function __construct(string $mensaje, string $email)
    {
        parent::__construct($mensaje);
        $this->email = $email;
    }

    public function enviar(): bool
    {
        echo "Enviando email a {$this->email}: {$this->mensaje}\n";
        return filter_var($this->email, FILTER_VALIDATE_EMAIL) !== false;
    }
}

class NotificacionSMS extends Notificacion
{
    private string $telefono;

    public function __construct(string $mensaje, string $telefono)
    {
        parent::__construct($mensaje);
        $this->telefono = $telefono;
    }

    public function enviar(): bool
    {
        echo "Enviando SMS a {$this->telefono}: {$this->mensaje}\n";
        return strlen($this->telefono) >= 9;
    }
}

$email = new NotificacionEmail('Bienvenido!', 'user@example.com');
$sms = new NotificacionSMS('Tu codigo: 1234', '+34666777888');

$email->enviar(); // Enviando email a user@example.com: Bienvenido!
$sms->enviar();   // Enviando SMS a +34666777888: Tu codigo: 1234

Ejercicio 3: Documentos

Crea Documento abstracta con titulo, metodo concreto getTitulo() y metodo abstracto exportar(): string. Implementa DocumentoPDF y DocumentoHTML que retornen formatos diferentes.

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

abstract class Documento
{
    protected string $titulo;
    protected string $contenido;

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

    public function getTitulo(): string
    {
        return $this->titulo;
    }

    abstract public function exportar(): string;
}

class DocumentoPDF extends Documento
{
    public function exportar(): string
    {
        return "[PDF] {$this->titulo}\n---\n{$this->contenido}\n---";
    }
}

class DocumentoHTML extends Documento
{
    public function exportar(): string
    {
        return "<html><head><title>{$this->titulo}</title></head>" .
               "<body><h1>{$this->titulo}</h1><p>{$this->contenido}</p></body></html>";
    }
}

$pdf = new DocumentoPDF('Informe', 'Contenido del informe...');
$html = new DocumentoHTML('Pagina', 'Contenido de la pagina...');

echo $pdf->exportar();
// [PDF] Informe
// ---
// Contenido del informe...
// ---

echo $html->exportar();
// <html><head><title>Pagina</title></head>...

¿Te está gustando el curso?

Tenemos cursos premium con proyectos reales y soporte personalizado.

Descubrir cursos premium