L'héritage est un concept fondamental de la POO. C'est d'ailleurs l'un des plus importants puisqu'il permet de réutiliser le code d'une classe autant de fois que l'on souhaite tout en ayant la liberté d'en modifier certaines parties.
Supposons que nous disposons d'une classe du nom de classe1. Si on crée une classe du nom de classe2 qui hérite de classe1, alors classe2 hérite de tous les membres (attributs et méthodes) qui constituent classe1. Autrement dit, si on instancie classe2, alors tous les attributs et méthodes de classe1 peuvent être appelés à partir de l'objet créé (l'instance de classe2). Bien entendu, il faut que les membres appelés soient publiques. Dans ce cas on dit que class1 est la classe mère et class2 est la classe fille.
Mot clé extends
Pour procéder à l'héritage, on fait appel au mot clé extends comme ceci:
<?php
class Mere{
}
class Fille extends Mere{
}
?>
Dans ce cas, la classe Fille hérite de la classe Mère.
Exemple:
<?php
class Mere{
public $attribut="Bonjour.";
public function methode1(){
$str=$this->attribut;
$str.=" Je suis la classe Mère.";
return $str;
}
}
class Fille extends Mere{
public function methode2(){
$str=$this->attribut;
$str.=" Je suis la classe Fille.";
return $str;
}
}
$objet=new Fille();
echo $objet->methode1();
echo "<br />";
echo $objet->methode2();
?>
On obtient alors:
Bonjour. Je suis la classe Mère.
Bonjour. Je suis la classe Fille.
Vous avez donc compris que $objet (l'instance de classe Fille) a eu accès à l'attribut publique $attribut et la méthode publique methode1 de la classe Mere.
Cependant, le principe d'encapsulation est remis en cause. En effet, tous les attributs doivent être privés et ce que nous avons fait dans ce code ne tient pas. Heureusement, les attributs ne peuvent pas être que privés ou publiques, mais aussi protégés grâce à la visibilité protected.
Visibilité protected
Afin de respecter le principe d'encapsulation, il faut que tous les attributs soient privés. Mais si on veut hériter d'une classe, alors les attributs privés ne sont plus réutilisables puisqu'ils sont reconnus uniquement au sein de la classe où ils sont déclarés.
La visibilité protected a été intégrée pour résoudre ce problème. En fait, elle se comporte à peu près comme private. Les attributs ne sont donc pas accessible de n'importe où comme c'est le cas pour public, mais seulement à l'intérieur de la classe dans laquelle ils sont déclarés et dans les classes qui héritent de cette classe là.
Le code précédent sera donc mieux comme ça:
<?php
class Mere{
protected $attribut="Bonjour.";
public function methode1(){
$str=$this->attribut;
$str.=" Je suis la classe Mère.";
return $str;
}
}
class Fille extends Mere{
public function methode2(){
$str=$this->attribut;
$str.=" Je suis la classe Fille.";
return $str;
}
}
$objet=new Fille();
echo $objet->methode1();
echo "<br />";
echo $objet->methode2();
?>
Le résultat escompté est le même:
Bonjour. Je suis la classe Mère.
Bonjour. Je suis la classe Fille.
Surcharge d'une méthode
Dans l'exemple précédent où la classe Fille hérite de la classe Mere, il est possible de définir une méthode du nom de methode1() dans la classe Fille. Bien entendu, cette méthode figure déjà dans la classe Mere et elle est supposée être héritée elle aussi car elle est publique. Le fait de déclarer une méthode du même nom dans la classe Fille s'appelle redéfinition de méthode ou surcharge de méthode.
Reprenons l'exemple précédent en surchargeant la méthode methode1():
<?php
class Mere{
protected $attribut="Bonjour.";
public function methode1(){
$str=$this->attribut;
$str.=" Je suis la classe Mère.<br />";
return $str;
}
}
class Fille extends Mere{
public function methode1(){
$str=$this->attribut;
$str.=" Je suis la classe Fille.<br />";
return $str;
}
}
$objet=new Fille();
echo $objet->methode1();
?>
Ce qui donne:
Je suis la classe Fille.
En effet, le fait de surcharger la methode1() dans la classe Fille a écrasé son code hérité de la classe Mere. Il est donc normal que seul le code de methode1() redéfinie dans la classe Fille soit exécuté.
Mais supposons que nous voulons quand même conserver le résultat retourné par methode1() dans la classe Mere et l'ajouter au résultat obtenu suite à la surcharge dans la classe Fille. Le code devrait donc ressembler à ceci:
<?php
class Mere{
protected $attribut="Bonjour.";
public function methode1(){
$str=$this->attribut;
$str.=" Je suis la classe Mère.<br />";
return $str;
}
}
class Fille extends Mere{
public function methode1(){
$str=Mere::methode1();
$str.=$this->attribut;
$str.=" Je suis la classe Fille.<br />";
return $str;
}
}
$objet=new Fille();
echo $objet->methode1();
?>
On obtient donc:
Bonjour. Je suis la classe Mère.
Bonjour. Je suis la classe Fille.
En fait, le résultat de methode1() de la classe Mere a été combiné au résultat de la méthode surchargée dans la classe Fille, et ce, grâce à l'appel statique de methode1() de la classe Mere à l'aide de l'opérateur de résolution de portée (::).
Il est possible à tout moment d'appeler statiquement les méthodes sans avoir besoin de les déclarer par le mot clé static.
Mot clé parent
Vous vous souvenez sûrement du mot clé self qui fait référence à la classe courante. Dans l'exemple précédent on a appelé la classe Mere par son nom dans la classe Fille pour appeler statiquement methode1(). Cette fois on va faire appel au mot clé parent qui désigne la classe dont on a hérité. Autrement dit, si nous sommes dans la classe Fille et on veut appeler statiquement methode1() de la classe Mere, au lieux de mettre Mere::methode1(), on peut mettre parent::methode1().
Le code précédent devient alors:
<?php
class Mere{
protected $attribut="Bonjour.";
public function methode1(){
$str=$this->attribut;
$str.=" Je suis la classe Mère.<br />";
return $str;
}
}
class Fille extends Mere{
public function methode1(){
$str=parent::methode1();
$str.=$this->attribut;
$str.=" Je suis la classe Fille.<br />";
return $str;
}
}
$objet=new Fille();
echo $objet->methode1();
?>
Contraintes sur l'héritage
A priori, on peut hériter de n'importe quelle classe sans restriction. Si on dispose de la classe A, on peut en hériter pour obrenir la classe B dont on peut hériter pour avoir la classe C et ainsi de suite. Cependant, on peut appliquer certaines contraintes pour autoriser ou non l'héritage d'une classe donnée, voir même obliger le développeur à hériter d'une classe particulière.
Classes et méthodes abstraites (Mot clé abstract)
Une classe abstraite et une classe dont on doit impérativement hériter. Autrement dit, on ne peut pas l'instancier directement. Pour ce faire, on définit la classe abstraite à l'aide du mot clé abstract.
Exemple:
<?php
abstract class A{
public function methode(){
echo "Bonjour";
}
}
class B extends A{
}
$objet=new B();
$objet->methode();
?>
Ce qui donne:
Bonjour
Dans ce cas, on a créé une classe abstraite du nom de A dont on a hérité pour créer la classe B. L'héritage dans ce cas et obligatoire car l'instanciation directe de la classe A est interdite.
il n'y a pas que les classes qui peuvent être rendue abstraites, mais les méthodes aussi. Dans ce cas, il faut obligatoirement que la classe soit abstraite aussi.
Une méthode abstraite est une méthode qui doit être redéfinie (ou surchargée) dans les classes filles. Pour définir une méthode abstraite on déclare son prototype composé du mot clé abstract suivi de la visibilité puis le mot clé function suivi du nom de la fonction et les parenthèses. On ne définit pas le corps d'une méthode abstraite, donc pas d'accolades.
Exemple:
<?php
abstract class A{
abstract public function methode();
}
class B extends A{
public function methode(){
echo "Bonjour";
}
}
$objet=new B();
$objet->methode();
?>
Ce qui donne:
Bonjour
Classes et méthodes finales (Mot clé final)
Une classe finale est une classe dont on ne peut pas hériter. Il faut donc l'instancier directement. Pour définir une classe finale il suffit de la précéder par le mot clé final.
Exemple:
<?php
final class A{
public function methode(){
echo "Bonjour";
}
}
class B extends A{
}
$objet=new B();
$objet->methode();
?>
Ce qui donne:
Fatal error: Class B may not inherit from final class (A) in index.php on line 8
En effet, l'héritage d'une classe finale est interdit.
Une méthode finale est une méthode que l'on peut pas redéfinir dans les classes filles. La déclaration de la méthode finale se fait normalement mais on précède sa visibilité par le mot clé final.
Exemple:
<?php
class A{
final public function methode(){
echo "Bonjour";
}
}
class B extends A{
}
$objet=new B();
$objet->methode();
?>
Ce qui donne:
Bonjour
Dans ce cas, on ne doit jamais surcharger methode() dans les classes filles.
Quelques règles à retenir
Si une méthode est abstraite alors la classe qui la contient doit être abstraie aussi.
Une classe abstraite peut contenir une méthode finale. Celle si ne doit pas être redéfinie lors de l'héritage.
Si une classe est finale, il est inutile d'y déclarer une méthode finale. Dans tous les cas on ne peut pas hériter de la classe.
Une méthode finale peut figurer dans une classe qui n'est ni finale ni abstraite.