Les classes abstraites et finales
Nous étudierons enfin le cas particulier des classes et méthodes finales qui participent à la sécurité du code en programmation orientée objet.
Présentation et déclaration des classes abstraites
Définition des classes abstraites
Les classes abstraites s'inscrivent davantage dans la sûreté de la programmation orientée objet. La première particularité d'une classe abstraite, c'est qu'elle ne peut être instanciée (et donc créer un objet). De cette affirmation, on en déduit logiquement qu'une classe abstraite est déclarée afin d'être dérivée par des classes concrètes.
Une classe abstraite se comporte comme une classe concrète typique. C'est-à-dire qu'elle peut déclarer des attributs et des méthodes traditionnels qui seront accessibles dans les classes dérivées. En fonction bien sûr de la visibilité choisie (public, private et protected) pour chacun des attributs et méthodes.
Jusque là, il n'y a aucun changement par rapport aux classes concrètes si ce n'est le fait que l'on ne puisse pas instancier les classes abstraites. C'est là qu'interviennent les méthodes abstraites. Nous verrons par la suite qu'une classe déclarée abstraire peut aussi définir des méthodes abstraites. Ces dernières devront obligatoirement être redéfinies dans les classes dérivées. C'est un moyen de s'assurer que la classe dérivée adoptera le comportement désiré.
Déclaration d'une classe abstraite
La déclaration d'une classe abstraite se réalise au moyen du mot-clé « abstract ». Prenons l'exemple d'une classe abstraite « EtreHumain » qui sera, par exemple, dérivée par deux classes concrètes «Homme » et « Femme ».
<?php
abstract class EtreHumain
{
/**
* Sexe de la personne
*
* @var string
*/
protected $sexe;
/**
* Nom de la personne
*
* @var string
*/
protected $nom;
/**
* Met à jour le nom
*
* @param string $nom
* @return void
*/
public function setNom($nom)
{
$this->nom = $nom;
}
/**
* Retourne le nom de la personne
*
* @param void
* @return string $nom
*/
public function getNom()
{
return $this->nom;
}
/**
* Retourne le sexe de la personne
*
* @param void
* @return string $sexe
*/
public function getSexe()
{
return $this->sexe;
}
}
?>
Vous remarquez que nous n'avons volontairement pas implémenté de constructeur dans cette classe puisque les classes abstraites ne peuvent être instanciée. Si nous essayons d'instancier cette classe pour créer un objet de type EtreHumain, nous obtiendrons le message d'erreur ci-après :
Intéressons nous à présent à l'autre subtilité des classes abstraites : les méthodes abstraites.
Déclaration et redéfinition des méthodes abstraites
Une méthode abstraite est aussi déclarée au moyen du mot-clé « abstract ». C'est une méthode partiellement définie dans la classe. En effet, lorsque l'on déclare une méthode abstraite, on ne définit que son prototype (sa signature). Les classes dérivées devront obligatoirement redéfinir entièrement (prototype + corps) toutes les méthodes abstraites de la classe parente.
Reprenons notre exemple précédent et ajoutons quelques méthodes abstraites à notre classe abstraite «EtreHumain ».
Déclaration de méthodes abstraites
<?php
abstract class EtreHumain
{
/**
* Sexe de la personne
*
* @var string
*/
protected $sexe;
/**
* Nom de la personne
*
* @var string
*/
protected $nom;
/**
* La personne fait du sport
*
* @abstract
*/
abstract function faireDuSport();
/**
* Divertit la personne
*
* @abstract
*/
abstract function seDivertir();
/**
* Met à jour le nom
*
* @param string $nom
* @return void
*/
public function setNom($nom)
{
$this->nom = $nom;
}
/**
* Retourne le nom de la personne
*
* @param void
* @return string $nom
*/
public function getNom()
{
return $this->nom;
}
/**
* Retourne le sexe de la personne
*
* @param void
* @return string $sexe
*/
public function getSexe()
{
return $this->sexe;
}
}
?>
>Notre classe dispose à présent de deux nouvelles méthodes abstraites faireDuSport() et seDivertir().Comme nous pouvons le constater, le corps de ces deux méthodes n'est pas défini. Nous devrons les définir dans les classes dérivées.
Remarque : toute classe qui définit une ou plusieurs méthodes abstraites doit obligatoirement être déclarées abstraite elle aussi.
C'est pourquoi nous allons à présent définir deux classes « Homme » et « Femme » qui hériteront toutes les deux des propriétés et méthodes de notre classe abstraite. Commençons simplement par les déclarer et tenter de les instancier.
Déclaration des classes dérivées
<?php
class Homme extends EtreHumain
{
/**
* Construit l'objet Homme
*
* @param string $nom Nom de l'homme
* @return void
*/
public function __construct($nom)
{
$this->nom = $nom;
$this->sexe = 'M';
}
}
class Femme extends EtreHumain
{
/**
* Construit l'objet Femme
*
* @param string $nom Nom de la femme
* @return void
*/
public function __construct($nom)
{
$this->nom = $nom;
$this->sexe = 'F';
}
}
?>
Tentons à présent d'instancier une des deux classes.
<?php
$oBob = new Homme('Bobby');
echo $oBob->getNom();
?>
Bien entendu, nous obtenons l'erreur suivante car nous n'avons pas redéfini les méthodes abstraites de la superclasse.
Ce message nous indique que nous devons soit redéfinir explicitement les méthodes abstraites de la superclasse ou bien rendre notre classe Homme abstraite. Comme nous souhaitons pouvoir instancier la classe dérivée, il ne nous reste que la première solution. Redéfinissons donc ces deux classes abstraites dans chacune des classes dérivées.
Redéfinition des méthodes abstraites dans les classes dérivées
<?php
class Homme extends EtreHumain
{
/**
* Construit l'objet Homme
*
* @param string $nom Nom de l'homme
* @return void
*/
public function __construct($nom)
{
$this->nom = $nom;
$this->sexe = 'M';
}
/**
* Affiche le sport de l'homme
*
* @param void
* @return void
*/
public function faireDuSport()
{
echo $this->nom .' fait de la boxe';
}
/**
* Affiche la distraction de l'homme
*
* @param void
* @return void
*/
public function seDivertir()
{
echo 'Soirée foot et bières';
}
}
class Femme extends EtreHumain
{
/**
* Construit l'objet Femme
*
* @param string $nom Nom de la femme
* @return void
*/
public function __construct($nom)
{
$this->nom = $nom;
$this->sexe = 'F';
}
/**
* Affiche le sport de la femme
*
* @param void
* @return void
*/
public function faireDuSport()
{
echo $this->nom .' fait du fitness';
}
/**
* Affiche la distraction de la femme
*
* @param void
* @return void
*/
public function seDivertir()
{
echo 'Shopping entre filles';
}
}
?>
Instancions maintenant chacune des deux classes.
Instances des classes Homme et Femme
<?php
$oAlice = new Femme('Alice');
$oAlice->faireDuSport();
echo '<br/>';
$oAlice->seDivertir();
echo '<br/>';
echo 'Sexe : ', $oAlice->getSexe();
echo '<br/><br/>';
$oBob = new Homme('Bobby');
$oBob->faireDuSport();
echo '<br/>';
$oBob->seDivertir();
echo '<br/>';
echo 'Sexe : ', $oBob->getSexe();
?>
Le résultat obtenu est bien celui attendu :
Pour les instances de la classe Femme, nous retrouvons bien la valeur F pour l'attribut $sexe ainsi que les méthodes abstraites redéfinies qui affiche correctement le fitness et le shopping. Quant aux instance de la classe Homme, le résultat est celui attendu. L'attribut $sexe prend bien la valeur M et les deux méthodes abstraites affichent bien le sport Boxe et le divertissement Soirée football.
Cas particuliers des classes et méthodes finales
Présentation des classes et méthodes finales
Jusqu'à maintenant nous avons présenté les classes et méthodes abstraites. PHP introduit un mécanisme supplémentaire pour assurer la surêté de la programmation. Il s'agit du mot-clé « final » qui peut-être appliqué soit à une classe ou bien à une méthode.
Lorsque l'on définit une classe comme « finale », cela signifie qu'elle ne pourra plus être dérivée par une sous-classe. Cela implique également que ses attributs et méthodes ne pourront plus être redéfinis. En revanche, si l'on applique le mot-clé « final » à une méthode d'une classe, alors c'est uniquement cette méthode qui ne pourra plus être redéfinie dans les classes dérivées.
En interdisant la dérivation d'une classe ou la redéfinition (surchage) des méthodes d'une classe, cela vous permet de vous assurer que le développeur ne contournera pas directement la logique que vous avez mise en place.
Déclaration de classes finales
Reprenons nos 3 classes précédentes. Nous décidons maintenant que les classes « Homme » et «Femme » ne pourront être dérivées. Nous les déclarerons donc comme étant finales. Ce qui nous donne :
<?php
final class Homme extends EtreHumain
{
// Suite du code de la classe
}
final class Femme extends EtreHumain
{
// Suite du code de la classe
}
?>
Tentons à présent de dériver l'une des deux classes et de l'instancier. Pour cela, il nous suffit d'écrire une classe « JeuneGarcon » qui hérite des propriétés et méthodes de la classe « Homme ».
<?php
class JeuneGarcon extends Homme
{
/**
* Construit l'objet JeuneGarcon
*
* @param string $nom Nom du jeune garcon
* @return void
*/
public function __construct($nom)
{
parent::__construct($nom);
}
}
?>
Bien entendu une erreur est générée puisque la classe Homme n'est plus dérivable du fait de sa déclaration "finale".
Remarque : une classe abstraite ne peut-être déclarée "finale". Une erreur de syntaxe sera générée. De plus, déclarer une classe abstraite et finale est synonyme d'un manque de logique puisque le but d'une classe abstraite est d'être dérivée par des classes filles.
Déclaration de méthodes finales
Etudions maintenant le cas des méthodes finales. Reprenons la classe abstraite « EtreHumain » et déclarons les méthodes « getNom() » et « getSexe() » comme finales.
<?php
abstract class EtreHumain
{
// Attributs et autres méthodes de la classe
// ...
/**
* Retourne le nom de la personne
*
* @param void
* @return string $nom
* @final
*/
public final function getNom()
{
return $this->nom;
}
/**
* Retourne le sexe de la personne
*
* @param void
* @return string $sexe
* @final
*/
public final function getSexe()
{
return $this->sexe;
}
}
?>
A présent les classes dérivées « Homme » et « Femme » ne peuvent plus redéfinir ces deux méthodes. Essayons de surcharger l'une de ces méthodes dans la classe « Homme ».
Redéfinition de méthodes abstraites
<?php
final class Homme extends EtreHumain
{
// Autres méthodes de la classe
// ...
/**
* Retourne le sexe
*
* @param void
* @return string sexe de l'homme
*/
public function getSexe()
{
return 'Masculin';
}
}
?>
Ici, la méthode « getSexe() » ne retourne plus la valeur de l'attribut protégé $sexe mais tente de renvoyer la chaine de caractères « masculin ». On s'en doute, la redéfinition de la méthode génère une erreur fatale par l'interprêteur PHP.
Conclusion
Tout au long de ce cours, nous avons découvert le mécanisme des classes et méthodes abstraites qui permettent de bénéficier des avantages de l'héritage et de s'assurer que les classes dérivées implémentent bien certaines actions.
Enfin nous avons étudié le cas particulier des classes et méthodes finales qui empêchent toute surcharge dans les classes dérivées et assurent une sécurité plus importante du code.