Interfaces et classes virtuelles
Par -Alexandre LEGOUT aka LAlex- le 1 juin 2004, 17:55 - Articles - Lien permanent
Plongé actuellement dans le passionant ouvrage qu'est Design patterns. Catalogue des modèles de conception réutilisables (merci Francis pour le conseil ;)), je constate que la base de chaque pattern consiste en un ensemble de classes abstraites ou d'interfaces, alors que peu de programmeurs non-habitués les utilisent réellement à leur plein potentiel. C'est pourquoi j'essaie modestement dans cet article de démontrer la réelle puissance apportée par ces outils. ![]()
Beaucoup de programmeurs non habitués à la POO ne se servent que trés peu des interfaces, ou des classes virtuelles (quand elles sont disponibles dans le langage utilisé). Cet article se propose de cerner l'utilité de tels outils dans la programmation orientée objet.
Les codes exemples sont partagés entre du PHP5 et de l'Actionscript. En effet, alors que le premier offre la possibilité de faire des classes abstraites, l'autre est à mon sens plus lisible et permet un typage fort des types simples ! ![]()
Définitions
Signature d'un fonction/méthode : Il s'agit de la "structure" d'une méthode. En gros, cela correspond aux arguments qu'elle prend en entrée, ainsi que leurs types, et le type de la valeur retournée.
Méthode abstraite (ou virtuelle) : Il s'agit d'une méthode qui ne contient pas d'implémentation. Une telle méthode oblige les classes qui vont hériter de la classe la contenant à implémenter une méthode dont la signature sera exactement la même.
Classe abstraite : Il s'agit d'une classe possédant au moins une méthode abstraite. Une classe abstraite n'est pas instanciable, du fait que l'ensemble de ses fonctionnalités n'est pas implémenté. Une classe abstraite permet de définir certaines implémentations par défaut, et à fournir la signature des méthodes non implémentées.
Pour l'Actionscript 2, Francis Bourre propose un pattern destiné à émuler les classes abstraites : http://www.tweenpix.net/archives/000316.html
Interface : L'interface est un ensemble de méthodes qui devront être implémentées dans les classes qui utilisent cette interface. Elle ressemble trés fortement à une classe abstraite qui ne contiendrait que des méthodes abstraites, et ne contient donc que des signatures de méthodes.
Différence entre classe abstraite et interface
Tout d'abord, la classe abstraite est utilisée par le biais de l'héritage. Il s'agit donc d'une relation de type "est un". Les classes qui hériteront d'une classe abstraite auront donc toutes un lien étroit concernant leur type. De plus, la plupart des langages n'autorisant plus l'héritage multiple, une classe ne peut souvent hériter que d'une seule classe, abstraite ou non. Il s'agit donc pour une classe abstraite de définir un "super type", dont d'autres types concrets pourront dériver.
Par exemple, on peut considérer que tous les véhicules à moteur se remplissent de la même manière : il suffit de mettre du carburant dans le réservoir. Tous les véhicules à moteur peuvent également être démarrés, mais chacun est démarré d'une manière qui lui est spécifique. Ainsi, il serait possible de créer une classe abstraite qui implémente le remplissage du réservoir, mais qui oblige chacune de ses sous-classes à implémenter la méthode de démarrage.
// Classe abstraite de véhicule a moteur
L'interface elle, spécifie quelles vont être les possibilités d'une classe, sans en fournir la moindre implémentation. Elle est donc garante des méthodes d'accés à un objet dont la classe implémente cette interface. On peut dire que l'implémentation d'une interface est une relation de type "possède les fonctionnalités de". D'ailleurs, les interfaces ont souvent des noms synonymes de "capable de" (en plus du 'I' qui commence généralement leur nom) : IDragable, IViewable, etc...
Par exemple, si l'on veut qu'un objet puisse être affiché, il est possible de créer une interface qui obligera la classe dont est issu l'objet d'implémenter une méthode display() ... Pour un objet qui doit pouvoir être déplacé par drag&drop, une interface pourra définir quelles sont les méthodes qui serviront à la manipuler pour cette tâche précise.
// Le replissage du reservoir possède une implémentation
// Par défaut
abstract class Vehicle {
private var $_maxFuel;
private var $_currentFuel;
// Méthode concrète
protected function fill($quantite) {
// Code pour remplir le réservoir
}
// Methode abstraite
// Les sous-classes devront l'implémenter
abstract public function start();
}
// Voiture
// Heritant de VahiculeAMoteur, on est obligé d'implémenter démarrer
// Si on ne le fait pas, une erreur va survenir
class Car extends Vehicle {
// Surcharge de la méthode abstraite
public function start() {
// Code pour démarrer la voiture
}
}// Interface définissant le comportement d'un objet
Utilisation
Les interfaces et classe abstraites n'ont un véritable sens que si elles sont associées à un langage typé fortement, ou du moins qui permet d'imposer un type d'objet dans les signatures de méthodes (comme le "type hint" de PHP5). En effet, plus qu'un "carcan" imposé au développeur de classes, elles sont surtout utiles aux classes clientes, qui vont avoir besoin d'être assurée d'un certain comportement des objets qu'elles vont utiliser.
En fait, pour pousser les choses plus loin, je dirait presque que pour une souplesse d'utilisation optimale, il ne faudrait utiliser que les interfaces et classes abstraites avec le typage fort. Il arrive souvent qu'un client ait besoin d'une implémentation précise, mais de toutes façons, le typage fort n'empêche pas une instance de sous-classe d'être utilisée, parfois avec une implémentation totalement différente.
Ainsi, quand une méthode va afficher un objet, elle n'a pas forcément besoin de savoir de quel type est cet objet, mais uniquement de savoir si l'objet est "affichable" ! Donc, si le fait d'être affichable revient à implémenter une interface qui demande à ce qu'une méthode display() soit présente, on utilisera cette interface pour le typage fort. Et l'objet passé à la méthode peut être de n'importe quel type, on est alors sûr qu'il disposera d'une méthode display(), dont la signature est définie par l'interface.
Prenons comme exemple une carte géographique. Cette carte est affichée en grand dans une fenêtre, et un ascenseur doit la déplacer. Pour cela, le déplacement de l'ascenseur recevra en paramètre l'objet qui représente la carte. Mais pour pouvoir la déplacer, il faut être certain que cette carte possède les méthodes qui vont nous permettre de la positionner. Il faut alors créer une interface imposant des méthodes d'accés. Ainsi, toute instance d'un classe qui implémente cette interface pourra être déplacée par l'ascenceur :
// que l'on peut déplacer à la souris
// Toute classe implémentant cette interface devra
// implémenter deux méthodes startDrag et stopDrag
interface IDragable {
public function startDrag():Void;
public function stopDrag():Void;
}
// Un curseur sur une jauge doit pouvoir
// être déplacé à la souris
class Cursor implements IDragable {
public function startDrag() {
// Code de début de déplacement
}
public function stopDrag() {
// Code de fin de déplacement
}
}// Interface de déplacement
interface IMovable {
// Positionnement de l'objet
public function moveToCoord(x:Number, y:Number);
// Déplacement de l'objet
public function moveByOffset(dx:Number, dy:Number);
}
// Classe d'une carte géographique
class Map implements IMovable {
// Clip correspondant à l'affichage de la carte
private var _targetMC:MovieClip;
// Methode de positionnement
public function moveToCoord(x:Number, y:Number) {
this._targetMC._x = x;
this._targetMC._y = y;
}
// Methode de déplacement
public function moveByOffset(dx:Number, dy:Number) {
this._targetMC._x += dx;
this._targetMC._y += dy;
}
}
// Scroller
class Scroller {
function moveObject(obj:IMovable) {
obj.moveByOffset(10,50);
}
}
Ainsi, la classe Scroller devient appliquable à n'importe quel objet qui possède les méthodes nécessaires à son déplacement. Il peut tout aussi biuen s'agir d'un dessin ou d'un texte. Ces classes n'ont rien à voir entre elles, mais leur comportement est unifié par l'utilisation de l'interface.
Conclusion
Comme vous pouvez le constater, les interfaces et classes abstraites offrent un moyen d'assouplir votre code, le rendant ainsi plus évolutif. Le fait d'utiliser un comportement et non plus une classe dans ses typages forts permet de généraliser une instruction à un ensemble bien plus large d'objets.
Alors que les interfaces sont souvent utilisées uniquement comme structure de classes lorsqu'on les connait peu, les associer au typage fort en font un outil trés puissant !
Commentaires
Il va me falloir beaucoup de pratique encore je pense et un peu de curiosité pour m'y mettre vraiment

Mais à force de vous voir en parler ainsi je pense que je vais m'y pencher sur mes prochains projets.
Merci pour cet article très intéressant
salut!
) , on veut contraindre le développeur à implementer des méthodes.
je pense que le probleme de leur non utilisation viens (pour moi) du fait qu'on a du mal à voir la différence entre les deux.
D'un point pratique (de maniere tres grossiere
Alors que comme tu le mets en évidence, ici la différence se situe surtout au niveau de la conception, comment on definit nos classes, leur roles ... Ce qui est pas forcement clair quand on attaque la POO..
Leur utilisation devient, je pense, incontournable quand on travaillà plusieurs sur un gros ensemble de classe (une framework ? ).
EN tout cas merci pour cet article.
Super article Lalex, c'est vrai que les deux concepts sont assez proches et j'ai toujours eu du mal avec, mais grâce à ton article j'ai compris de suite, c'est cool.
Merci encore
Youpie, j'ai compris. Bon, je completerai mon article et le mettrai sur mon site. Un grand merci LAlex, toujours là quand il faut.
J'aime beaucoup les petits résumés comme ça.
A garder sous le coude en cas de trou de mémoire...
Interessant billet, du moins kan j'en aurait comprit le sens
j'ai encore pas mal de définition à associé à certains mots looool
Bon article Lalex, vive le polymorphisme!
Tiens Timothee vient d'ouvrir un blog... hop dans mes bookmarks !
vive l'abstraction!
J'ai du mal à bien comprendre qu'est-ce qu'est un typage fort...
Est-ce que cela implique totale dissociation entre le type et la classe ?
Comme je le dit dans l'article, il s'agit surtout de type hint (je ne vois pas vraiment de traduction française :$) . C'est le fait de pouvoir imposer un type de paramètre et de retour dans la déclaration des fonctions !
Ca correspond en PHP à
// La fonction doit recevoir obligatoirement un objet de type "Voiture"En AS2, ca s'écritfunction start(Voiture $myCar) {
// Code ici
}
function demarrer(myCar:Voiture) {// Code ici
}
++ ^^
Ok, super merci en tout cas, tout ce que tu as fait m'est vachement utile... Et dire que tu m'a piqué le titre de mon prochain article, quelle honneur pour moi !

Bon, je vais travailler moi
Dis, est-ce qu'une classe peut implémenter plusieurs interface en AS2?
Niveau typage fort, peut-on considérer qu'une classe est un type? Car, certains langages (surtout les plus récents) dérivent toutes les classes d'une classe Object, que ce soit pour des classes évoluées que les classes définissant les int, uint, string, etc... De plus en plus, on en arrive à un rapproochement entre types et classes. Et c++, on pouvait dire qu'une classe était un type, pas l'inverse, et c#, la réciprocité se tient, car le langage est d'assez au niveau. Tout comme l'as2 (ces deux langages ont presque les mêmes spécifications :P)
++
Fil des commentaires de ce billet