Débutant

Comprendre les concepts de la programmation orientée objet

Comprendre les concepts de la programmation orientée objet

Il y a tant à dire sur la Programmation Orientée Objet (POO) et qu’il faudrait un cours entier pour tout aborder et tout maîtriser sur ce sujet. Mais je vais tenter de vous expliquer les concepts fondamentaux dans cet article ce qui vous permettra d’y voir plus clair et de vous donner de bonnes bases.

Malgré cela, l’article est un peu plus long que d’habitude.

C’est quoi la Programmation Orientée Objet ?

Normalement vous avez dû coder jusqu’à présent en programmation procédurale. Vous connaissez donc le principe des fonctions où les données sont facilement accessibles et modifiables.

Vous avez peut-être remarqué que ça peut vite devenir l’anarchie et un beau bordel tant il est très rapide et tentant de créer des fonctions et d’en mettre un peu partout dans nos applications.

La programmation orientée objet c’est un peu l’inverse. Les fonctions sont remplacées par des objets où les données sont plus difficilement accessibles et modifiables.

Si on se base sur Wikipédia, la POO est un paradigme de programmation (voyez cela comme une méthode) élaboré par 2 Norvégiens au début des années 1960.

Le principe est assez simple : la programmation orientée objet consiste à créer et faire interagir des briques logicielles que l’on appelle objets.

Et un objet représente une idée, un concept ou toute entité du monde physique. Un ordinateur est un objet. L’article que vous lisez est lui-même un objet et l’on peut même dire que vous et moi, nous sommes des objets. Nous verrons un peu plus loin de manière plus approfondie comment fonctionnent les objets.

Notez quand même que la façon de penser les problèmes et la conception de vos programmes sera totalement différente par rapport à la méthode procédurale.

Pourquoi utiliser la Programmation Orientée Objet ?

Tout d’abord, vous pouvez considérer que la quasi-totalité des langages de programmation permet de faire de la POO.

Comme vous créez des objets dans des buts bien précis et qu’ils sont capables d’interagir entre eux, cela vous donnera de facto une structure de code beaucoup plus clair, mais aussi plus modulable et plus facile à maintenir.

Ce sera également plus facile de débugger, car vous saurez plus précisément quel objet pose directement problème suivant votre contexte applicatif.

Qu’est-ce qu’un objet ?

Comme je vous l’ai dit plus haut, en POO, tout est objet et c’est pareil dans la vie réelle. Prenons pour exemple l’ordinateur que vous utilisez pour lire cet article.

L’ordinateur est un objet :

  • il possède ce qu’on appelle des propriétés caractérisées sous forme de données. Pour l’ordinateur, c’est une marque, un clavier, une souris, de la RAM, un processeur, un disque dur, etc…
  • il peut réaliser des actions. L’ordinateur peut s’allumer ou s’éteindre, réaliser des traitements.
  • Il peut interagir avec d’autres objets. L’ordinateur doit interagir avec un écran (qui est un objet) pour fonctionner, un humain (qui est un objet aussi) peut allumer ou éteindre l’ordinateur.

Bien entendu, la logique veut que l’on peut avoir plusieurs objets (donc plusieurs ordinateurs si on reprend notre exemple) mais qui auront certainement des caractéristiques différentes (les propriétés seront les mêmes mais les données seront différentes). On parlera alors d’instance.

Comprendre la notion de classe

Juste avant, je vous parlais d’instance. Et bien en programmation orienté objet, un objet né de l’instance d’une classe. C’est en fait la base de la programmation orientée objet.

Pour vous représenter une classe, on peut dire que c’est le schéma ou le plan qui nous permet de générer notre objet. On peut générer autant d’objets que l’on souhaite à partir de notre classe. Mais il faut savoir une chose : un objet généré à partir d’une classe ne peut pas changer de classe en cours de route.

Une classe est un catalogue de variables (propriétés) et de fonctions internes (actions) qui interagissent entre eux pour donner un résultat. Il est donc possible d’utiliser des propriétés dans vos méthodes et vous pouvez même appeler vos méthodes dans d’autres méthodes.

En fait, dans le cas d’une classe et de l’orientée objet on ne les appelle pas les actions des fonctions, mais des méthodes.

Si on reprend l’exemple de notre ordinateur, voici ce que cela pourrait donner en PHP :

Class Ordinateur { 
   public $marque; 
   public $modele; 
   public $ram; 
   public $processeur; 
   public $disque_dur;

   public function Allumer() { 
   
   } 
   
   public function Eteindre() { 

   } 

}

 

Le principe d’encapsulation

L’encapsulation est l’un des principes non négligeables de la POO. Mais en quoi cela consiste ?

C’est relativement simple en fait : cela consiste à cacher le fonctionnement interne de votre objet en imposant à l’utilisateur de l’objet de passer par vos méthodes.

Cela permet notamment 2 choses :

  • La sécurisation des données de votre objet, car l’utilisateur est obligé d’utiliser les méthodes que vous lui mettez à disposition
  • Réaliser des traitements internes à l’objet sans que l’utilisateur ne le sache. Car oui certains traitements n’ont pas besoin d’être visibles pour l’utilisateur de l’objet, car soit cela n’a pas d’intérêt ou parce que tout simplement il s’en fout. C’est ce qu’on appelle l’abstraction.

Reprenons notre classe et mettons en pratique le principe d’encapsulation pour bien comprendre :

Class Ordinateur { 
    private $marque;  
    private $modele;  
    private $ram;  
    private $processeur;  
    private $disque_dur;  

    public function setMarque($nom_de_la_marque) {
        $this->marque = $nom_de_la_marque; 
    } 
    
    public function setModele($nom_du_modele) {
        $this->modele = $nom_du_modele; 
    } 
    
    public function setRAM($qte_de_ram) {
        $this->ram = $qte_de_ram; 
    } 
    
    public function setProcesseur($nom_du_processeur) {
        $this->processeur = $nom_du_processeur; 
    } 
    
    public function setDisqueDur($qte_du_disque_dur) {
        $this->disque_dur = $qte_du_disque_dur; 
    } 
    
    public function setMarque($nom_de_la_marque) {
        $this->marque = $nom_de_la_marque; 
    } 
    
    public function Allumer() { 
        $this->ChargementDuBios();
    } 

    private function ChargementDuBios() { 
        $this->ChargementOS(); 
    } 

    private function ChargementOS() { 

    } 
    
    public function Eteindre() { 

    } 

}

En PHP, le mot-clé “$this->” fait référence à la classe en elle même pour pouvoir y appeler un attribut ou une méthode

La sécurisation des données

Il y a 3 niveaux de visibilités dans l’encapsulation à savoir :

  • public : les méthodes et attributs avec ce mot-clé sont accessibles aisément par l’utilisateur de l’objet, mais également par la classe elle-même.
  • protected : les méthodes et attributs avec ce mot-clé sont accessibles uniquement depuis la classe elle-même et ses classes héritées (je vous parlerais de l’héritage dans le point suivant)
  • private : les méthodes et attributs avec ce mot-clé sont accessibles uniquement depuis la classe elle-même

Si vous avez été attentif, vous avez dû remarquer que j’ai passé tous les attributs de la classe en privé. Même si techniquement il est possible de mettre des attributs en public, il n’est pas conseillé de le faire si vous souhaitez bénéficier de toute l’efficacité de l’encapsulation.

Donc, gardez cet automatisme : si un utilisateur veut changer un attribut de ma classe, il doit passer par une méthode.

J’ai donc créé des méthodes qui commence par “Set” puis suivies du nom de mon attribut pour que l’utilisateur puisse enregistrer sa valeur. Bien entendu dans cet exemple, je ne contrôle pas ce que l’utilisateur entre comme valeur mais il faut le faire.

Vous pouvez par exemple contrôler que la valeur entrée soit une chaîne de caractères ou bien un nombre compris dans un intervalle de données. Vous avez compris le principe. En faisant tous les contrôles voulus vous assurez l’intégrité des données de votre classe.

De même que si vous souhaitez donner la possibilité à l’utilisateur de la classe de récupérer la valeur d’un attribut à votre utilisateur, vous devez créer cette méthode.

Par exemple, si je veux lui donner la marque je pourrais créer une méthode de ce genre :

public function getMarque() { 
    return $this->marque; 
}

Le principe d’abstraction

Vous avez également remarqué que j’ai rajouté des méthodes en « private ». La plupart sont vides de contenu, mais ce n’est pas un problème pour l’exemple.

En revanche ce qui est intéressant, c’est la méthode « public » qui appelle une méthode « private » et qui elle même appelle une méthode « private »  :

 
    public function Allumer() { 
        $this->ChargementDuBios();
    } 

    private function ChargementDuBios() { 
        $this->ChargementOS(); 
    } 

    private function ChargementOS() { 

    } 

Un ordinateur a l’allumage doit charger le Bios puis l’OS pour pouvoir être utilisé. L’utilisateur se fiche complément de l’ordre de l’allumage et encore moins de ce qu’il passe réellement : ce qui l’intéresse c’est le résultat de l’allumage et pas comment cela fonctionne pour arriver à ce résultat.

C’est le principe d’abstraction.

Grâce à ce genre de système, l’utilisateur réalise tout un tas de traitement sous l’effet d’une méthode sans qu’il s’en rende compte.

La notion d’héritage

Il est possible de faire ce qu’on appelle de l’héritage entre les objets que l’on définit au moment où l’on crée la classe. En quoi cela consiste ?

L’héritage vous permet de faire hériter toutes les méthodes et les attributs d’une classe « parent » (celle qui donne l’héritage) à la classe enfant (l’héritier de la classe « parent »).

Concrètement, votre classe enfant bénéficiera de tout ce qui est possible dans la classe « parent » en plus de ses propres méthodes et attributs.

Enfin en grande partie, mais pas tout à fait comme vous allez le voir.

Chose importante : une classe ne peut pas hériter de plusieurs classes au moment de sa définition mais une classe peut hériter d’une classe ayant déjà fait un héritage (en cascade donc).

L’utilité du mot-clé protected

Vous vous souvenez que je vous ai parlé de la sécurisation des données et qu’il y avait 3 niveaux. Ils ne sont pas qu’utiles pour les utilisateurs de la classe mais aussi pour l’héritage. La classe héritière est comme un utilisateur de la classe.

Donc elle ne peut accéder qu’aux éléments « public ». Mais il peut arriver parfois que l’on ait besoin que les classes héritées puissent accéder à des méthodes privées sans qu’un utilisateur ne puisse lui-même y accéder.

C’est là que « protected » est utile. « Protected » va maintenir la privation pour un utilisateur mais permettra à une classe enfant d’y accéder quand même.

Réécrire les méthodes de la classe « parent »

Un autre point de l’héritage est que l’on peut réécrire les méthodes de la classe « parent » dans la classe enfant. Et quand je dis réécrire, j’entends par là complètement. Vous pouvez changer complètement le fonctionnement d’une méthode. Mais vous pouvez bien entendu compléter une méthode existante en appelant le traitement du « parent » et en complétant par le vôtre.

Il y a juste une formalité à respecter lorsque l’on réécrit une méthode. Vous ne pouvez redescendre le niveau de sécurité de la méthode que vous voulez réécrire. On ne peut que l’augmenter ou laisser le niveau de sécurité imposé par la méthode du « parent ». Par exemple : vous ne pouvez pas passer une méthode « private » en « protected » dans votre classe enfant mais vous pouvez passer une méthode « public » en « private » si vous le souhaitez.

Voici un autre exemple dans la continuité de notre classe Ordinateur pour mettre en pratique ce que je viens d’énoncer :

Class OrdinateurPortable extends Ordinateur { 
    private $ecran 

    public function setEcran($taille_ecran) {
        $this->ecran = $taille_ecran; 
    } 
    
    public function getEcran() {
        return $this->ecran; 
    } 
    
    public function Allumer() { 
        $this->AllumerEcran();
        parent::Allumer();
    } 

    private function AllumerEcran() { 
        
    } 
    
    private function EteindreEcran() { 
        
    } 
    
    public function Eteindre() { 
        $this->EteindreEcran();
        parent::Eteindre();
    } 

}

Notre classe « Ordinateur » possède comme action d’allumer ou éteindre. C’est notre classe « parent ». J’ai donc créé une classe « enfant » appelée « OrdinateurPortable » qui récupère naturellement les attributs et les méthodes de la classe « parent » « Ordinateur ».

Notre « OrdinateurPortable » possédera donc une marque, un modèle, une quantité de RAM, un processeur et un disque dur. Un nouvel attribut et 2 nouvelles méthodes sont apparus pour gérer l’écran.

Pour gérer l’écran lors de la phase d’allumage et d’extinction du « OrdinateurPortable », j’ai donc ajouté 2 méthodes privées « AllumerEcran » et « EteindreEcran ».

J’ai ensuite incorporé l’appel de ses méthodes dans la réécriture des méthodes « Allumer » et « Eteindre » (provenant de la classe « parent »).

Pour « Allumer, j’ai appelé en premier lieu « AllumerEcran puis le traitement issu de la classe « parent » (on utilise en PHP la syntaxe parent:: pour appeler le traitement et le résultat de la méthode.

Pour « Eteindre » , c’est le exactement le même combat. On appelle en premier « EteindreEcran » suivit du traitement de « Eteindre » de la classe « parent ».

La notion d’interface

Une dernière notion que je voulais aborder avec vous : ce sont les interfaces.

Les interfaces sont des genres de classes contenant un ensemble de méthodes publiques démunies de toute logique. En clair, les méthodes sont déclarées et vides de codes. Une interface ne contient pas d’attributs et ne peut pas être instanciée, juste implémentée.

On peut imager en disant qu’une interface est une sorte de plan où les classes vont pouvoir s’appuyer et qu’elles seront obligées de respecter.

Mais alors on est en droit de se demander à quoi cela peut servir ?

Fonctionnellement parlant à rien du tout ! Elles ne contiennent aucune logique. Par contre au niveau conceptuel et structurel d’une application, çela peut être intéressant.

Voyons cela avec un exemple :

interface Ordinateur { 
    
    public function Allumer();
    public function Eteindre();
    public function faireDesTraitements();


}

On déclare une interface Ordinateur avec 3 méthodes publiques. On va maintenant l’implémenter

Class OrdinateurPortable implements Ordinateur { 
    private $ecran 

    public function setEcran($taille_ecran) {
        $this->ecran = $taille_ecran; 
    } 
    
    public function getEcran() {
        return $this->ecran; 
    } 

    private function ChargementDuBios() { 
        $this->ChargementOS(); 
    } 

    private function ChargementOS() { 

    }
    
    public function Allumer() { 
        $this->AllumerEcran();
        $this->ChargementDuBios();
    } 

    private function AllumerEcran() { 
        
    } 
    
    private function EteindreEcran() { 
        
    } 
    
    public function Eteindre() { 
        $this->EteindreEcran();
    } 

}

Dans l’état, la classe « OrdinateurPortable » ne pourra pas fonctionner, car l’interface exige qu’une méthode appelée « faireDesTraitements » doit être implémentée et ce n’est pas le cas dans mon exemple. Pour résoudre le problème, il suffit d’implémenter la méthode (et de la coder bien évidemment).

Donc une classe qui implémente une interface vous donne la garantie que cette classe contienne toutes les méthodes de l’interface.

Remarquez également que pour implémenter une interface, j’ai utilisé le mot-clé implements qui est au pluriel. Car il est tout à fait possible d’implémenter plusieurs interfaces pour une classe.

Partager ce contenu
  • 43
    Partages

Leave a Comment

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.