Utilisation des mecanismes des langages objets - classes abstraites

Description

Après la lecture de références sur Java et autres langages objets où il est listé plus qu'expliqué l'usage des différents mots clés de l'approche objet, je suis resté sur ma faim de débutant à chaque fois. En effet à quoi sert tout ça? ou plutôt pourquoi ça existe?
La JavaDoc ou autres aides en ligne se contente souvent d'exposer une syntaxe, et les forums ou autres FAQ montrent des exemples qui n'en disent souvent pas assez. Les outils donnés par les langages objet ont une génèse, et une utilité. Essayons de percevoir ça.

Introduction

J'essaierai de traiter dans une série de tutoriels les mécanismes des langages objet au fur et à mesure que je progresse dans leur compréhension.
La surcharge, les interfaces et l'utilisation du pattern adapter... pourraient être les suivants

Les classes abstraites

Une classe abstraite, qu'est-ce que c'est? (reprise rapide de ce qu'on trouve partout)

C'est une classe où on laisse l'implémentation de certaines méthodes "vides" à ses descendantes.

Les méthodes à laisser "vides" doivent être spécifiées abstract et ne pas avoir de corps {i.e.pas de {code} ). Par exemple:

public abstract void methode();

Du coup, la classe doit être précédée du mot clé abstract sinon le compilateur empêche la compilation..

public abstract class ClasseAbstraite {....code...}

Les classes abstraites ça sert quand? (on ne trouve pas toujours ça)

Je ne pense pas pouvoir être exhaustif sur les usages des classes abstraites car je suis relativement neuf en Java, mais en tentant de concevoir un code "beau", on réfléchit à ce que rien ne soit écrit plusieurs fois (factorisation du code). Et la factorisation, la réutilisation du code, c'est la raison d'être de tous les mécanismes propres aux langages objet, notamment les classes abstraites.
Lorsque, dans une classe on veut appeler une méthode du niveau de conception des descendantes dans une méthode appartenant au niveau de conception de la classe mère:(ouf ..)
Exemple: On imagine que l'on souhaite coder une méthode commune à tous les animaux dans une classe animal : la méthode sAlimenter. Sachant que tous les animaux vont obtenirNourriture, ingurgiter,digérer, on aimerait écrire :

class Animal{
    ....code...
    public voidsAlimenter(){ //code du niveau de l'animal
    //le tralala qu'on ne voudra pas recoder ailleurs si c'est du niveau de Animal
        obtenirNourriture(); //à programmer au niveau de chaque descendant d'Animal
        ingurgiter(); //à programmer au niveau de chaque descendant d'Animal
        digérer(); //à programmer au niveau de chaque descendant d'Animal
   }
}

Les méthodes obtenirNourriture, ingurgiter,digérer doivent être disponibles au niveau du code de l'animal, ce qui évitera ainsi de recoder pour chaque sorte d'animal la méthode sAlimenter. Or on ne peut pas décider d'une implémentation des trois methodes appelées dans sAlimenter pour que l'organisation du code corresponde au niveau de concept d l'animal. Mais les langages objets disposent de l'outil classe abstraite, qui permet de coder en fonction de méthodes vides comme ici.
Il suffi de rajouter après le code précédent la signature des méthodes que l'on souhaite appeler sans en connaitre l'implémentation (pour réserver un point d'entrée pour l'implémentation, lorsqu'elle sera disponible dans une instance de classe descendante):

public abstract void obtenirNourriture();
public abstract void ingurgiter(); 
public abstract void digérer();

Or pour que la compilation se fasse, il faut rajouter abstract devant la déclaration de la classe
Au final le code complet de notre classe abstraite :

abstract class Animal{
    ....code...
    public voidsAlimenter(){ //code du niveau de l'animal
    //le tralala qu'on ne voudra pas recoder ailleurs si c'est du niveau de Animal
        obtenirNourriture(); //à programmer au niveau de chaque descendant d'Animal
        ingurgiter(); //à programmer au niveau de chaque descendant d'Animal
        digérer(); //à programmer au niveau de chaque descendant d'Animal
   }
   public abstract void obtenirNourriture(); //points d'entrée pour le code des classes enfants
   public abstract void ingurgiter(); 
   public abstract void digérer();
}

Question à deux balles pour mieux comprendre

oui mais pourquoi pas une simple classe avec des implémentations "creuses" comme public void obtenirNourriture() {}; et qui seraient surchargées par les classes filles?

Pourquoi pas mais :

  • Si on laisse à quelqu'un d'autre le soin de coder les classes filles, comment être certain que les méthodes seront implémentées? La classe abstraite est un moyen de s'en assurer, car le compilateur veillera pour nous.
  • Quand on appellera un objet de type Chien en tant qu'Animal (inventaire d'une ménagerie par exemple), ce sont ces méthodes vides qui seront appelées, et pas celles des instances.

Exemple :

Chien instanceDeChien;
Chat instanceDeChat;
Serpent instanceDeSerpent;
Animal[] menagerie=[instanceDeChien,instanceDeChat,instanceDeSerpent];
Animal[1].ingurgiter(); //ne fera rien si surcharge d'une méthode vide, mais ingurgitera à la maniere de Chat si dérivation d'une classe abstraite.

Conclusion

Voilà n'hésitez pas à rajouter des questions pour la rubrique Questions à deux balles, ou vos remarques critiques qui me permettront de ne pas laisser trainer trop d'erreurs.

Ce document intitulé « Utilisation des mecanismes des langages objets - classes abstraites » issu de CodeS SourceS (codes-sources.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.
Rejoignez-nous