8- L'héritage

Comme nous l'avons dit précédemment, un des gros avantages de la programmation orientée objet est la factorisation du code, principe extrêmement important dans le développement qui consiste à ne pas réécrire inutilement du code déjà écrit.

Pour ne pas se répéter inutilement, la POO mets à notre disposition deux notions géniales : l'héritage et le polymorphisme.

L'héritage de classes est le fait de pouvoir créér une classe qui contient déjà les attributs et méthodes non privées de la classe dont elle hérite. Par exemple, si je crée une classe Véhicule qui a la méthode avancer() et que ma classe Voiture hérite de ma classe Véhicule, elle pourra utiliser sa méthode avancer() sans même la déclarer.

De plus, si je crée une classe Berline qui hérite de ma classe Voiture, ma berline pourra utiliser les attributs et méthodes de Véhicule et de Voiture !

Pour déclarer l'héritage d'une classe en PHP, il faut utiliser le mot-clé extends.

Par exemple, si Voiture hérite de Véhicule :

// déclaration des classes
class Vehicule
{
    public function avancer() : string
    {
        return "Vroum !";
    }
}

class Voiture extends Vehicule
{

}

// utilisation des objets
$voiture = new Voiture();
echo $voiture->avancer(); // écrit "Vroum !"

Il est possible d'hériter d'une classe mais de réécrire les méthodes héritées. Cela s'appelle la réécriture comme nous l'avons dit précédemment.

Par exemple, si ma voiture fait autre chose que vroum :

// déclaration des classes
class Vehicule
{
    public function avancer() : string
    {
        return "Vroum !";
    }
}

class Voiture extends Vehicule
{
    public function avancer() : string
    {
        return "Ratatata !";
    }
}

// utilisation des objets
$voiture = new Voiture();
echo $voiture->avancer(); // écrit "Ratatata !"

Comme nous l'avons dit précédemment, pour qu'un objet puisse accéder aux attributs de la classe de laquelle il hérite, il faut utiliser le modificateur d'accès protected. Il ne sert à rien de mettre tous les attributs private ou protected, cela dépend simplement de l'usage que l'on en aura : si on veut transmettre l'attribut à l'héritage, on le déclarera comme protected, et private le cas échéant.

Exemple d'utilisation d'un attribut protected

// déclaration des classes
class Moteur{

}

class PareBrise{

}

class Vehicule
{
    protected $moteur;

    public function __construct(Moteur $moteur)
    {
        $this->moteur = $moteur;
    }

    public function getMoteur() : Moteur
    {
        return $this->moteur;
    }
}

class Voiture extends Vehicule
{
    protected $pareBrise;

    public function __construct(Moteur $moteur, PareBrise $pareBrise)
    {
        Parent::__construct($moteur);
        $this->pareBrise = $pareBrise;
    }

    public function getPareBrise() : PareBrise
    {
        return $this->pareBrise;
    }
}

// utilisation des objets
$moteur = new Moteur();
$pareBrise = new PareBrise();
$voiture = new Voiture($moteur, $pareBrise);

var_dump($voiture->getMoteur(), $voiture->getPareBrise());
//object(Moteur)#1 (0) {
//}
//object(PareBrise)#2 (0) {
//}

Dans cet exemple, nous avons :

  • Un attribut protected (moteur) issu de la classe Vehicule
  • Un attribut protected (pareBrise) issu de la classe Voiture
  • Un constructeur de la classe Vehicule qui prend en paramètre un Moteur et qui initialise son attribut $moteur.
  • Un constructeur de la classe Voiture qui utilise le constructeur de sa classe parent (et qui initialise donc son $moteur grâce au $moteur passé en paramètre) et qui prend en paramètre un PareBrise pour initialiser son attribut $pareBrise.
  • Deux accesseurs sur les attributs $moteur et $pareBrise. Notez que la $voiture peut accéder au $moteur car il est protected.

Il est également possible d'interdire la réécriture ou la surcharge d'une méthode, et l'héritage d'une classe en les déclarant final.