Saltar a contenido

Herencia

PHP soporta herencia simple, de manera que una clase solo puede heredar de otra, no de dos clases a la vez. Para ello se utiliza la palabra clave extends. Si queremos que la clase A herede de la clase B, se hará de la siguiente forma:

class A extends B
classDiagram
    class A {
        +atributo1
        +metodo1()
    }

    class B {
        +atributo2
        +metodo2()
    }

    A <|-- B

El hijo hereda los atributos y métodos públicos y protegidos.

Cada clase en un archivo

Como ya hemos comentado, deberíamos colocar cada clase en un archivo diferente para posteriormente utilizarla mediante include. En el siguiente ejemplo, se han colocado juntas para facilitar su legibilidad.

Por ejemplo, tenemos una clase Producto y una Tv que hereda de Producto:

ejemploHerencia.php
<?php
class Producto {
    public $codigo;
    public $nombre;
    public $nombreCorto;
    public $PVP;

    public function mostrarResumen() {
        echo "<p>Prod:".$this->codigo."</p>";
    }
}

class Tv extends Producto {
    public $pulgadas;
    public $tecnologia;
}

Se pueden utilizar las siguientes funciones para averiguar si hay relación entre dos clases:

  • get_parent_class(object): string
  • is_subclass_of(object, string): bool
ejemploHerencia2.php
<?php
include 'ejemploHerencia.php';

$t = new Tv();
$t->codigo = 33;
if ($t instanceof Producto) {
    echo $t->mostrarResumen();
}

$padre = get_parent_class($t);
echo "<br>La clase padre es: " . $padre;
$objetoPadre = new $padre;
echo $objetoPadre->mostrarResumen();

if (is_subclass_of($t, 'Producto')) {
    echo "<br>Soy un hijo de Producto";
}

Sobreescribir métodos

Se pueden crear métodos en los hijos con el mismo nombre que el padre, cambiando su comportamiento. Para invocar a los métodos del padre parent::nombreMetodo()

ejemploHerencia.php
<?php
class Tv extends Producto {
   public $pulgadas;
   public $tecnologia;

   public function mostrarResumen() {
      parent::mostrarResumen();
      echo "<p>TV ".$this->tecnologia." de ".$this->pulgadas."</p>";
   }
}

Constructor en hijos

En los hijos no se crea ningún constructor de manera automática. Por lo tanto, si no lo hay, se invoca automáticamente al del padre. En cambio, si se define en el hijo, se debe invocar al del padre de manera explícita.

ejemploconstructorhijo7.php
<?php
class Producto {
    public string $codigo;

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

    public function mostrarResumen() {
        echo "<p>Prod:".$this->codigo."</p>";
    }
}

class Tv extends Producto {
    public $pulgadas;
    public $tecnologia;

    public function __construct(string $codigo, int $pulgadas, string $tecnologia) {
        parent::__construct($codigo);
        $this->pulgadas = $pulgadas;
        $this->tecnologia = $tecnologia;
    }

    public function mostrarResumen() {
        parent::mostrarResumen();
        echo "<p>TV ".$this->tecnologia." de ".$this->pulgadas."</p>";
    }
}

PHP 8 introdujo "promoción de propiedades del constructor", que te permite definir y asignar propiedades directamente desde los parámetros del constructo

ejemploconstructorhijo8.php
<?php
class Producto {
    public function __construct(private string $codigo) { }

    public function mostrarResumen() {
        echo "<p>Prod:".$this->codigo."</p>";
    }        
}

class Tv extends Producto {

    public function __construct(
        string $codigo,
        private int $pulgadas,
        private string $tecnologia)
    {
        parent::__construct($codigo);
    }

    public function mostrarResumen() {
        parent::mostrarResumen();
        echo "<p>TV ".$this->tecnologia." de ".$this->pulgadas."</p>";
    }
}
Esto hace lo mismo que el código en PHP 7, pero más limpio. PHP 8 crea automáticamente las propiedades, $codigo, $pulgadas y tecnologia, y las asigna.

Clases abstractas

Las clases abstractas obligan a heredar de una clase, ya que no se permite su instanciación. Se define mediante abstract class NombreClase. Una clase abstracta puede contener propiedades y métodos no abstractos, y/o métodos abstractos.

AbstractProducto.php
<?php
// Clase abstracta
abstract class Producto {
    private $codigo;
    public function getCodigo() : string {
        return $this->codigo;
    }
    // Método abstracto
    abstract public function mostrarResumen();
}

Recordando clases abstractas

En el módulo profesional de Programación ya se introdujo el concepto de clase abstracta. Recuerda que, cuando una clase hereda de una clase abstracta, debe implementar obligatoriamente los métodos que el padre ha marcado como abstractos.

ejemploabstracta2.php
<?php
include 'AbstractProducto.php';

class Tv extends Producto {
    public $pulgadas;
    public $tecnologia;

    public function mostrarResumen() { //obligado a implementarlo
        echo "<p>Código ".$this->getCodigo()."</p>";
        echo "<p>TV ".$this->tecnologia." de ".$this->pulgadas."</p>";
    }
}

$t = new Tv();
echo $t->getCodigo();

Clases finales

Son clases opuestas a las abstractas, ya que evitan que se pueda heredar una clase o método para sobreescribirlo.

ejemploclasesfinales.php
<?php
class Producto {
    private $codigo;

    public function getCodigo() : string {
        return $this->codigo;
    }

    final public function mostrarResumen() : string {
        return "Producto ".$this->codigo;
    }
}

// No podremos heredar de Microondas
final class Microondas extends Producto {
    private $potencia;

    public function getPotencia() : int {
        return $this->potencia;
    }

    // No podemos implementar mostrarResumen()
}

Actividades

Sobre las actividades

A lo largo de estas actividades, vamos a trabajar progresivamente con las clases Persona y Empleado, refinando su definición y ampliando su funcionalidad. Partiremos de una estructura básica, y paso a paso iremos incorporando conceptos clave de la programación orientada a objeto.

  • 📝 AC 308. (RA3 / CE3b CE3d / IC1 / 3p) - Se ha de realizar una copia del ejercicio AC307 y realizar una serie de modificaciones. Se ha de crear una clase Persona que sea padre de Empleado, de manera que Persona contenga el nombre y los apellidos, y en Empleado quede el salario y los teléfonos.

  • 📝 AC 309. (RA3 / CE3b CE3d / IC1 / 3p) - Se deben realizar modificaciones en AC308. Debemos implementar en Persona el método estático toHtml(Persona $p), y modificar en Empleado el mismo método toHtml(Persona $p), pero debemos cambiar la firma para que reciba una Persona como parámetro. Para acceder a las propiedades del empleado con la persona que se reciben como parámetro, debemos de comprobar su tipo

    <?php
        class Empleado extends Persona {
            /// resto del código
    
            public static function toHtml(Persona $p): string {
                if ($p instanceof Empleado) {
                    // Aqui ya podemos acceder a las propiedades y métodos de Empleado
                }
            }
        }
    
  • ⚓ AR 310. (RA3 / CE3b CE3d CE3g / IC1 / 3p) - Nos han solicitado una serie de modificaciones sobre AC309. En Persona se ha de añadir un atributo edad. A la hora de saber si un empleado debe pagar impuestos, lo hará siempre y cuando tenga más de 21 años y dependa del valor de su sueldo. Se ha de modificar todo el código necesario para mostrar y/o editar la edad cuando sea necesario.

  • 🔬 AP 311. (RA3 / CE3b CE3d CE3g / IC1 / 3p) - Siguiendo con las mejoras en las clases Personay Empleado, debemos añadir nuevos métodos que hagan una representación de todas las propiedades, de forma similar a los realizados en HTML, pero sin que sean estáticos, de manera que obtenga los datos mediante $this.

    • function public __toString(): string
  • 🧪 PR 312. (RA3 / CE3b CE3c CE3d CE3g / IC2 / 5p) - El CTO del proyecto necesita empezar con él. Para ello se ha de diseñar un programa que contenga las siguientes clases: Persona, Producto, Carrito y PedidoComercial. Una vez realizado esto, se han de generar las clases:

    • EmpleadoTienda, Comercial y Cliente que heredan de Persona.
    • Pedido que hereda de Carrito.