Tag Archives: POO

Les interfaces en Java

Voici un petit cours/tutos sur les interfaces en java. Le but n’est pas d’expliquer en profondeur le fonctionnement des interfaces, cela ayant déjà été fait par pas mal de gens.Pour la théorie je recommande deux ouvrages, le premier, chez Eyrolles, “Programmer en Java” de Claude Delannoy, Ingénieur au CNRS, dans la collection BestOf. Une véritable bible du développeur Java à petit prix (800 pages, 19 € prix couverture) déjà vendu à 250 000 exemplaires, traduit en plusieurs langues (ce qui est remarquable pour un livre technique de ce type). Ce serait un crime de ne pas se jeter dessus. Un second, plus générique, toujours chez Eyrolles, “La programmation Orientée Objet, Cours et Exercices en UML2, Java, C#, C++, Python, PHP et Linq” par Hughes Bersini, Ingénieur physicien et Directeur du Laboratoire sur l’intelligence artificielle à l’ULB (Université Libre de Bruxelles), un pédagogue exceptionnel et ouvert que j’ai d’ailleurs eu l’occasion de rencontrer il y a quelques mois dans le cadre d’un salon sur le logiciel libre. 600 pages +/- à 35 € prix couverture. Bon, ceci étant dit, je pars du principe que la théorie de l’héritage, le polymorphisme, l’abstraction et les règles de bases qui sous-tendent l’OO sont connues (pas forcément maîtrisées). Passons au but de l’article, montrer en quoi les interfaces sont réellement un outil/concept utile à la conception d’un logiciel et offre une réelle abstraction supplémentaire par rapport aux classes abstraites et à l’héritage de classes abstraites. En effet, à la lecture de beaucoup de cours, les interfaces sont souvent très bien expliquées dans la théorie mais pas dans la pratique, ce qui, au final, donne la sensation au lecteur qu’une interface est juste une classe abstraite sans l’implémentation des méthodes de classes.
Pourquoi ? Voici un exemple standard vraiment simple qui ressort dans presque tous les cours/tutos :

Humain.java :

Garçon.java :

Main.java :

Cet exemple est tiré directement d’un cours, considéré comme une référence, paru chez O’reilly (comme la plupart de leurs livres). Ceci ne montre rien des interfaces à part le fait que l’on implémente une interface Humain dans Garçon et que l’on redéfinit la méthode d’interface respirer() dans Garçon. Ensuite on crée un objet Garçon(), nommé Jean puis, on fait respirer Jean. Dans cet exemple, la plupart des lecteurs se disent : “Un Garçon est un humain et il respire… Ok… Mais moi dans mon code, vu que tous les humains respirent, garçons ou filles, et de la même manière, je ne vais pas redéfinir la méthode respirer chaque fois, ça ne me sert a rien ! Hum… Je vais plutôt faire une classe abstraite Humain et Garçon héritera de Humain !” Et cette réaction est légitime, car en effet cet exemple (et la plupart de ceux que l’on trouve) est dénué de sens pour quelqu’un qui aborde le sujet et sort en général du chapitre sur les abstractions. (Dans tous les livres que j’ai, quel que soit le langage, si on parle d’abstraction, le chapitre suivant sera 9 fois sur 10 les interfaces…).

J’ai donc fait un exemple pour rendre justice aux interfaces et que vous compreniez en quoi la redéfinition de méthode est vraiment utile. Voici la situation à modéliser.

“Je veux un tableau d’objet, ces objets seront des êtres vivants, je veux pouvoir les faire mourir et ressusciter selon le type d’êtres vivants.”

Un besoin très simple, J’ai donc une interface Vivant, cette interface a 3 méthodes, la première retourne une valeur booléenne sur l’état de l’être (true ou false), la seconde le tue, la troisième le ressuscite. L’interface est donc un dénominateur commun de méthodes à tous les êtres vivants. Maintenant il faut implémenter cette interface. Nous allons créer basiquement deux classes, Humain et Chat. Sur ces deux types d’êtres vivants on peut implémenter sans aucun souci les comportements demandés dans le besoin et définis dans l’interface. La où la redéfinition, grâce aux interfaces, sera utile, c’est au niveau de la résurrection. En effet, un humain ne peut ressusciter (ou alors il faudrait créer une classe supplémentaire Saint), par contre un chat a plusieurs vies, on peut donc dire qu’il peut “ressusciter”.

Voici le code que l’on obtient avec l’utilisation correcte des interfaces pour ce besoin :

Vivant.java :

Humain.java

Chat.java :

Main.java

Alors, pour le code de l’interface, rien de bien mystérieux. Sur humain on a donc implémenté Vivant, on a défini un attribut boolean livin sur son état, la méthode is_alive() retourne simplement livin, la méthode kill() vérifie que l’humain n’est pas déjà mort, si ce n’est pas le cas (que livin est a true), on le tue, sinon on prévient qu’il est déjà mort et la méthode ressusciter renvoie simplement que JP n’est pas JC. Pour la classe chat, on a le même attribut livin que sur humain, on a juste un attribut intégré en plus contenant le nombre de vies restant au chat, la méthode is_alive a le même comportement que Humain, kill également, par contre ressusciter a un comportement différent. Sur le premier if, on vérifie que le chat a encore des vies et qu’il est mort, si c’est le cas, on le ressuscite en passant livin a true et on lui enlève une vie, si on veut le ressusciter mais qu’il n’est pas mort ou alors qu’il ne lui reste plus de vie on prévient l’utilisateur selon la situation. Enfin, dans main.java, c’est à ce niveau que tout va se jouer. Dans la plupart des cours on aurait créé un objet Chat et un objet Humain, mais les interfaces jouent un rôle d’abstraction, ce qui veut dire que l’on peut créer (comme demandé dans le besoin) un tableau d’objets du type de l’interface. On peut donc créer un tableau de vivants. On initialisera ensuite chaque objet selon le type de vivant, par exemple un chat ou un humain. Et là où l’abstraction est vraiment utile c’est que, vu que ces classes implémentent les mêmes méthodes, celles définies dans l’interface, on peut dans une boucle for par exemple, appeler ses méthodes sans connaître le type ‘final’ de l’objet sur lequel on travaille. On peut donc tuer tous les êtres vivants du tableau sans savoir si on a affaire à un humain, un chat, un chien, un cochon, de plus ce n’est pas le but, on veut tuer les êtres vivants du tableau, on se moque de savoir de quel être vivant il s’agit. Voici le dernier point qui est souvent mal détaillé dans les cours, l’abstraction que les interfaces proposent, ce qui, en plus de forcer la redéfinition, est la différence fondamentale entre classes abstraites et interfaces. Dans main.java on créé donc un array de size[2], un tableau de vivants que l’on nomme etres_vivant, on crée à l’index 0 un chat et à l’index 1 un Humain. Ensuite on a un for(x;x;x) qui parcourt le tableau et qui sur chaque objet, fait des appels de méthodes :

1. Savoir s’il est vivant

2. Le faire mourir

3. Le ressusciter

4. Le ressusciter

5. Le tuer

6. Le ressusciter

7. Savoir s’il est vivant

Ce qui nous donne dans la sortie standard :

C’est tout pour les interfaces, j’espère avoir pu expliquer clairement une des vraies utilités des interfaces. Les exemples ici sont en Java, mais le fonctionnement et la sémantique restent les mêmes dans la plupart des langages OO permettant l’utilisation d’interface (C# par exemple).

Sur ce…