Nous voici dans la deuxième partie de notre compréhension des objets avec PHP5. La première partie était consacrée à la définition d’une classe, d’un objet et du constructeur. Celle-ci nous a permis de poser les bases dans la Programmation Orientée Objet (POO). Ainsi d’en devenir un langage de POO à part entière, PHP5 fournit néanmoins désormais les outils nécessaires à ceux qui souhaitent choisir cette orientation. La manipulation d’objets n’est pas une obligation dans le plupart des cas mais pourrait devenir une nécessité pour de gros projets.
Cette seconde partie va nous permettre d’aborder des notions très importantes en terme d’objet, je dirais même des notions fondamentales qu’il convient de toujours garder à l’esprit lorsque vous pensez POO (peu importe le langage) : Encapsulation, Héritage et Polymorphisme.
Je pense que rien qu’à la lecture de ces trois termes vous êtes en train de vous demander mais de quoi s’agit-il ?! Rien ne sert de paniquer je vais vous expliquer au mieux ceci dans la suite de cet article.
Note : Nous reprendrons dans cet article les notions abordées dans la partie précédente, et nous garderons le nom des classes, attributs et méthodes.
Avant d’aborder les trois notions, je vais reprendre le code et le compléter en ajoutant la classe Contact
qui va nous servir dans la suite de cet article.
class CarnetAdresse {
public $persons = array();
public $type = "buisiness";
public $contact = null;
function __construct( $type = null, Contact $contact = null) {
$this->type = $type;
$this->contact = $contact;
}
function resume() {
$ret = "Type du carnet d’adresses : {$this->type} <br />";
$ret .= "CarnetAdresse: ".count($this->persons)."<br />";
return $ret;
}
}
class Contact {
var $firstname = null;
var $name = null;
function __construct($firstname, $name) {
$this->firstname = $firstname;
$this->name = $name;
}
function getInfo() {
return "Bonjour je m’appelle $this->firstname $this->name";
}
}
[/sourcecode]
Encapsulation
L’encapsulation est le processus d’abstraction des caractéristiques de votre objet ou de n’importe quels champs de cet objet. C’est derrière ce terme que se cache le concept même de l’objet, c’est-à-dire de réunir au sein d’une même entité les champs et les méthodes.
Le but d’utiliser ce processus est qu’il vous permet de contrôler strictement contrôler l’accessibilité de vos données, la manipulation, mais également le peuplement. Par défaut, en PHP, tous les champs et les méthodes d’une classe sont ce que nous appelons public
, ce qui signifie qu’ils sont tous accessibles à partir des instances de notre classe. Ainsi, l’attribut $persons
est rendu public et il peut être utilisé à partir de notre classe CarnetAdresse
.
Cela fonctionne très bien parce que nous pouvons facilement saisir nos contacts de notre carnet d’adresses et obtenir des informations à leur sujet. Le problème est que nous pouvons maintenant remplacer la valeur des contacts dans notre carnet d’adresses puisque nous pouvons y accéder.
$buis->persons = "Bonjour tout le monde";
[/sourcecode]
Tout d’un coup nos contacts ne sont plus des contacts mais une simple chaîne. Si cela se produit, les conséquences peuvent être désastreuses, conduisant à toutes sortes de problèmes dans votre programme. D’une part, $persons
devrait être un tableau, et dès que vous allez essayer d’accéder à la valeur de ce tableau, vous aurez une valeur erronée (première lettre de la chaîne de caractère par exemple), voire même une erreur (si vous parcourez ce tableau à l’aide du mot clé foreach
). En outre, notre programme prévoit d’être en mesure d’accéder à la méthode getInfo()
à partir du tableau des contacts, ce qui est impossible à partir de la chaîne de caractères. Pour éviter ces problèmes, nous allons utiliser l’encapsulation pour éviter que tout le monde modifie la variable $persons
, et fournit une nouvelle façon d’y accéder en utilisant nos propres règles.
Les mots-clés que vous allez utiliser avec l’Encapsulation sont public
, private
, protected
. Comme mentionné ci-dessus, toute l’accessibilité aux propriétés et aux méthodes sont publiques par défaut.
- public : signifie qu’une méthode/propriété est visible et utilisable/modifiable par tous les objets et instances de la classe et de ses classes dérivées (voir la section suivante sur l’Héritage)
- private : signifie qu’une méthode/propriété est uniquement utilisable/modifiable par la classe elle-même, donc ni dans les classes dérivées, ni par aucun objet
- protected : signifie qu’une méthode/propriété est uniquement utilisable/modifiable par la classe elle-même et dans ses classes dérivées, mais par aucun objet
Reprenons notre exemple. Imaginons que l’on change le niveau d’accessibilité private
à la propriété : $persons
. Comme décrit ci-dessous je ne vais plus pouvoir accéder à partir de mon objet $buis
, cela se traduira par une erreur, mais je peux encore l’utiliser dans le code de la classe, nous protégeant ainsi l’affectation de la valeur à une simple chaîne de caractères.
class CarnetAdresse {
private $persons = array();
…
[/sourcecode]
Ensuite, nous devons fournir un moyen pour la classe de partager la valeur de propriété. Pour ce faire, nous allons créer une fonction à l’intérieur de notre classe qui retourne la valeur de nos personnes.
public function getPersons() {
return $this->persons;
}
[/sourcecode]
Maintenant, que nous avons fourni un moyen d’avoir la liste de toutes les personnes de notre carte d’adresse, il est tout à fait possible d’appeler la méthode getPersons
retournant cette liste, et nos données seront enregistrées en sécurité dans notre classe, avec aucun moyen de modifier la valeur avec toute autre valeur qui n’est pas un tableau.
$buis = new CarnetAdresse();
$persons = $buis->getPersons();
[/sourcecode]
C’est bien beau tout ça, le système est fiable, mais comment ajouter des personnes dans notre annuaire ? C’est un vrai problème… Nous avons certes réglé le problème pour ne pas modifier la valeur de notre propriété $persons
, et nous sommes en mesure de récupérer sa valeur enregistrée en créant une fonction publique qui la renvoie. Cela signifie que nous pouvons aussi utiliser une fonction pour créer une méthode permettant d’ajouter de nouvelles personnes à notre carnet d’adresses.
public function addPerson(Contact $contact) {
$this->persons[] = $contact;
}
[/sourcecode]
Maintenant, nous pouvons ajouter d’autres contacts dans notre carnet d’adresses, et il n’y a aucune chance pour que la valeur de notre propriété persons
soit autre chose qu’un contact (Contact
).
$buis = new CarnetAdresse("buisiness");
$contact = new Contact("yohann", "poiron");
$buis->addPerson($contact);
echo $buis->resume();
print_r($buis->getPersons());
[/sourcecode]
Tout ce processus est un exemple d’encapsulation. Nous avons encapsulé la propriété persons
dans cet exemple. C’est généralement une bonne idée d’encapsuler toutes vos propriétés, même si vous souhaitez autoriser l’accès complet par la suite. Ce n’est pas seulement une bonne pratique de codage, mais il rend également votre code plus cohérent et plus facile à suivre.
Pour conclure, l’encapsulation permet de garder une cohérence dans la gestion de l’objet, tout en assurant l’intégrité des données qui ne pourront être accédées qu’au travers des méthodes visibles.
Héritage
L’héritage en terme de POO est fondamental. Ce processus vous permet, en fonctions des besoins, de faire évoluer une classe sans la modifier en créant une classe dérivée, également nommée classe enfant, fille ou encore sous-classe. Celle-ci reprend les caractéristiques de sa classe parente ou directement de la classe de base. La classe dérivée hérite des caractéristiques telles que les propriétés ou toutes les fonctions de la classe parente, et vous lui ajoutez des fonctionnalités supplémentaires.
Ce principe est utilisé le plus souvent lorsque vous avez un groupe d’objets qui partagent des caractéristiques similaires mais qui ont certaines spécificités qui les séparent.
C’est typiquement le cas dans notre carnet d’adresses. Ce dernier peut être composé de contacts « humains » mais également des « sociétés ». Tous nos contacts partagent une caractéristique : le nom. Nous allons donc créer une hiérarchie de classes en spécialisant chaque classe selon nos besoins.
Note : La limite avec PHP5, contrairement à d’autres langages, est qu’il n’autorise que l’héritage simple, une classe ne pouvant hériter que d’une seule classe parente.
Pour ajouter cette hiérarchie dans notre programme, nous devons simplement créer deux classes enfants.
class Contact {
var $name = null;
function __construct($name) {
$this->name = $name;
}
}
[/sourcecode]
Avec ce code, nous pouvons maintenant créer un objet contact générique qui a un nom et qui nous donne des infos. En soi ce n’est pas une vraie révolution, cependant nous pouvons maintenant instancier tous les objets de notre carnet d’adresses et hériter des caractéristiques de la classe Contact
. Pour ce faire, il suffit de faire suivre le nom de la nouvelle classe du mot-clé extends
puis du nom de la classe parente.
class Human extends Contact {
var $firstname = null;
}
class Company extends Contact {
var $business_sector = null;
}
[/sourcecode]
Désormais, avec ce code lorsque nous créons un nouvel objet human
, il aura les mêmes caractéristiques que la classe générique. Étant donné que les propriétés et fonctions sont héritées nous n’avons pas besoin de les redéfinir dans les classes enfants.
En revanche, dans le corps de la classe enfant, il est possible de redéfinir les propriétés et les méthodes de la classe parente, sauf si elles sont déclarées en private
ou final
. Il est également possible d’accéder aux propriétés et aux méthodes redéfinies de la classe parente en les faisant précéder du mot-clé parent::
. Nous allons compléter notre cas d’étude en redéfinissant le constructeur, en utilisant le constructeur parent, et on enrichira la fonction d’affichage.
class Human extends Contact {
var $firstname = null;
function __construct($name, $firstname) {
parent::__construct($name);
$this->firstname = $firstname;
}
function getInfo() {
$infos = parent::getInfo();
$infos .= "Je suis une personne, dont mon identité est $this->name $this->firstname";
return $infos;
}
}
class Company extends Contact {
var $business_sector = null;
function __construct($name, $business_sector) {
parent::__construct($name);
$this->business_sector = $business_sector;
}
function getInfo() {
$infos = parent::getInfo();
$infos .= "Je suis une société, dont mon secteur d’activité est $this->business_sector";
return $infos;
}
}
[/sourcecode]
Notre objet « humain » / « société » va utiliser sa propre fonction getInfo()
. Vous allez sûrement vous demander pourquoi faire hériter les classes Human
et Company
, alors qu’on écrase la fonction ? La meilleure raison c’est que certaines propriétés, telle que $name
n’aura pas besoin d’être écrasé et fonctionnera pour tout type de contact. Cela vous évite d’avoir à écrire du code identique !
$human = new Human("POIRON", "Yohann");
echo $human->getInfo();
$company = new Company("Openxtrem", "tertiary");
echo $company->getInfo();
[/sourcecode]
Maintenant, nous pouvons créer autant de types de contacts que nous voulons pour notre carnet d’adresses et hériter de leurs caractéristiques de base à partir de notre classe Contact
. Ce concept entier est appelé héritage. S’il y a une chose à retenir, c’est que l’idée de base derrière l’héritage est que les objets similaires partagent des propriétés communes. Donc, en créant une classe « générique », nous avons un patron pour nos classes dérivées. Ce processus d’héritage peut bien sûr être répété. Autrement dit, il est tout à fait possible de déclarer des classes dérivées à partir d’une classe dérivée elle-même.
Polymorphisme
Le polymorphisme est dérivé de deux mots grecs : polloí, qui signifie plusieurs et morphos, forme.
En informatique, le polymorphisme est l’idée d’autoriser le même code à être utilisé avec différents types, ce qui permet des implémentations plus abstraites et générales. [Wikipedia]
Le polymorphisme traite de la capacité de l’objet à posséder plusieurs formes. Des langages mûrs tels que C++ ou JAVA intègrent ce concept de manière native. PHP5 permet également d’intégrer le polymorphisme !
Concrètement, vous devez écrire une classe de base qui définit les attributs et les fonctions de l’objet et autant de sous-classes que vous le souhaitez. Cette notion découle directement de l’héritage (cf. la section précédente).
Le langage PHP n’étant pas typé (vous n’êtes pas obligé, à la création d’une variable, de spécifier le type de données quelle va recevoir), les avantages du polymorphisme sont forcément moins évidents que dans d’autres langages…
Conclusion
Voici un petit récapitulatif de cette partie :
- L’héritage permet d’éviter de réécrire des fonctions déjà existantes qu’il suffit de réadapter
- L’héritage permet d‘adapter et/ou spécialiser des classes déjà existantes afin qu’elles répondent à vos besoins, et cela, sans modifier les classes originales
- Sur des projets volumineux, une architecture de classes/héritage est plus simple à gérer
- L’encapsulation permet si plusieurs développeurs travaillent ensemble, que l’un développe la classe et l’autre l’utilise, ce dernier n’aura besoin de connaître l’interface de la classe, c’est-à-dire la liste des méthodes et attributs qu’il peut utiliser
Vous connaissez maintenant la plupart des notions relatives à la POO existant dans PHP5. Il est possible que vous ne soyez pas encore à l’aise avec l’ensemble de ces notions, c’est pourquoi vous ne devez pas hésiter à tester les syntaxes vues et prendre le temps de vous familiariser avec ce nouveau vocabulaire.
Vous devriez êtes en mesure de commencer la construction et la conception d’applications de base en utilisant les techniques de la POO. C’est pourquoi nous verrons une troisième partie consacrée à la mise en application pour comprendre quand et comment vous pouvez réellement utiliser ces techniques en interrogeant une base de données MySQL et un service Web.
C’est un style de programmation puissant et complexe, mais qui devient rapidement un allié de taille une fois maîtrisé. N’ayez donc pas peur de passer du temps à le travailler.
Partagez-vous mon point de vue ?