begin process at 2012 02 15 11:12:58
  Trouver un code source :
 
dans
 
Accueil > 

Tutoriels

 > 

Api

 > UTILISATION DES MECANISMES DES LANGAGES OBJETS - CLASSES ABSTRAITES

UTILISATION DES MECANISMES DES LANGAGES OBJETS - CLASSES ABSTRAITES


 Information sur le tutoriel

Note :
7 / 10 - par 2 personnes
7,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

 Description

Après la lecture de refé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 q quoi sert tout ça? ou plutôt pourquoi ça existe?
La javadoc ou autres aides en ligne se contentent souvent d'exposer une syntaxe, et les forum 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.

Tutorial

J'essaierai de traiter dans une série de tutorials 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 specifié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 precé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 methode 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 methode 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 methodes obtenirNourriture, ingurgiter, digérerdoivent être disponibles au niveau du code de l'animal, ce qui evitera 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 methodes 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'implementation (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 declaration 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 implementation "creuses" comme   "public void obtenirNourriture() {};"  et qui seraient surchargées par les classes filles?

Pourquoi pas mais :
1.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.
 
2.
Quand
on appelera un objet de type Chien en tant qu'Animal (inventaire d'une menagerie 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.

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.





 Historique

01 août 2006 01:46:36 :
retouches (titre, lignes inutiles supprimées... Ambiguités reformulées)

Commentaires

Commentaire de rebellus le 29/01/2007 07:49:26

salut tu peux faire un exemple sur un fichier java stp merci d'avance

Commentaire de jannoman le 12/02/2007 19:21:19

pour simplifier, je pense que les classs abstraites servent à faire des types disjonctifs (pour ceux qui connaissent CamL) : si tu as une variable qui peut faire soit un entier, soit un caractère (exemple : pour traiter des choses comme "3 + 4 -1", tu pourrais faire ce genre de distinction), et bien on aimerait avoir un type qui dise entier ou char, mais en java on fait souvent une classe contenant 2 champs (int n, char c) (plus un troisieme pour indiquer lequel des deux est celui en question (int selecteur qui vaut 0 si c'est n qui importe, 1 si c'est c).
mais le plus propre c'est de faire avec les classes abstraites :
classe Element dont vont hériter tes signes ou entiers, et la tu peux avoir un tableau, (ou mieux un arbre !) qui va coder ta formule, et dont les sommets/feuilles sont des Element.

Commentaire de pouicky le 12/02/2007 22:21:48

"pour simplifier", est une entrée en matière un peu trompeuse dans ta remarque.
Pour quelqu'un qui ne code pas ses propres opérateurs ni ses propres types, l'exemple semble loin d'être une simplification. Ceci dit, c'est une ouverture sur le monde CAML qui permet d'entrevoir la portée de la programmation objet.
Mais après avoir lu ton commentaire plusieurs fois, je ne vois pas vraiment si ton exemple illustre le polymorphisme de types et d'opérateurs, ou les expressions regulières... et on est loin de l'exemple que j'avais choisi sur des animaux. Et je ne vois pas non plus (parce que je ne code pas à un niveau aussi élémentaire) comment il caractérise l'utilisation des classes abstraites. Une illustration plus détaillée serait la bienvenue.

Commentaire de ouafaa83 le 15/07/2007 16:14:43

salut,
j'ai comme exposé:classe abstraite et illustration avec la classe "object" et des classes "Enveloppes", stp tu peut me donner quelques idées, conseils a propos de se sujet? merci d'avance

Commentaire de pouicky le 15/07/2007 23:19:47

Mais de quoi parle ton sujet?
Ta question est trop vague, et soit il s'agit de modifier le source  d'un TP, pour faire un découpage "beau" du code. ça peut se faire en rangeant le code dans le niveau conceptuel le plus adapté, comme par exemple, par la création de classes abstraites.
Les classes abstraites contiendront une pseudo implémentation de méthodes dont l'appel se traduira par l'appel de la même méthode dans les sous classes concrètes, avec leur implémentation particulière. Personne ne peut te répondre sur les classes object et enveloppes en dehors du contexte précis de ton exposé.
Ne s'agirait il pas d'un wrapper,tes enveloppes?

Commentaire de ouafaa83 le 22/07/2007 23:53:12

oui, c'est un wrapper, en fait,je veux savoir tout sur les classes enveloppe et la classe object,l'interet,les particularitées,l'utilisation...et bien sur des exemples exécutables.
merci

Commentaire de pouicky le 23/07/2007 01:20:00

Sur le lien suivant, une implémentation du pattern décorateur, ou encore wrapper sur la classe TableModel:
http://java.sun.com/docs/books/tutorial/uiswing/examples/components/TableSorterDemoProject/src/components/TableSorter.java
Le code est poussé, mais la description, faite par des pros de chez sun (en anglais) est assez claire et décrit bien la conception de la classe en tant que décorateur de TableModel;

Il s'agit d'ajouter des fonctionnalités à une classe TableModel, en créant une classe capable de la "décorer" avec des fonctionnalités de tri supplémentaires.

Cette classe est dérivée d'une classe abstraite, AbstractTableModel, et utilise pour tableModel celui passé au constructeur,qu'on veut décorer.

Ce pattern (décorateur ne me semble pas typique de l'usage des classes abstraites, mais tous les avis sont possibles.)

Un tour vers les design patterns et notamment le pattern template, te fera voir un usage des classes abstraites:
http://abrillant.developpez.com/tutoriel/java/design/pattern/introduction/#L3.3

Pour la classe Object en java, c'est la mère de toutes les classes - une conséquence pratique, par exemple: Un tableau de type Object[] permet de stocker tout type d'objet.
--> chercher javadoc/object dans google

Voilà quelques pistes. Pour les exemples executables, il faudrait ecrire un petit morceau de traitement à améliorer en utilisant tour à tour un décorateur, des classes abstraites...(pourquoi pas des animaux? ;)
Du code redondant au départ, sans hierarchie de classes:
Classes Chat, Oiseau, Poisson avec des méthodes voler, nager, marcher.
Des instances de chaque animal et puis un boucle sur chaque animal pour les faire se déplacer --> nécéssité de faire un appel à une méthode commune, implémentée différemment pour chaque animal.
Soit on redefini une hierarchie de classes avec un classe abstraite "Animal" héritée diffémment pour chaque classe "Animal"
Ou bien on garde chaque méhode (pour compatibilité avec un existant par exemple) et on utilise un adaptateur qui oblige à définir dans chaque classe la méthode commune.
Si on cherche de la robustesse, on utilise alors des contraintes d'implémentation d'interfaces pour ces classes (Interface AnimalMobile, par exemple, avec methode "seDéplacer" à implémenter). Le code ne se lancera pas sans que toutes les classes n'implémentent leurs interfaces.
En jouant avec des sorties texte "je vole, je me déplace, je suis décoré avec un manteau pour chat...), ça devrait etre vivant et digeste."

Commentaire de pouicky le 23/07/2007 01:43:10

ici une tres bonne explication (en anglais, forcément, quand on cherche "wrapper")
http://www.exciton.cs.rice.edu/JAvaResources/DesignPatterns/DecoratorPattern.htm

Le paragraphe de description est assez clair et précis. On y lit que Le décorateur est assimilable à l'objet qu'il décore car il implemente les memes méthodes (plus d'autres). Il est dit aussi que les décorateurs sont souples d'utilisation, car ils peuvent se combiner sur un décoré sans que le reste du code ne soit impacté.
Enfin, il semble que le décorateur soit un descendant d'une même classe abstraite que le décoré. (il y aurait donc un rapport étroit entre wrapper et classes abstraites)
Bon courage.

Commentaire de ouafaa83 le 23/07/2007 11:46:19

excusez moi, je ne vois pas encore la relation entre les classes abstraites et la classe object d'une part, et entre les classes astraites et les classes enveloppes d'autre part!!
durant ma recherche sur le sujet,j'ai trouvé pour la classe Object que c'est,comme vous l'avez dit, la mère de toutes les classes,c-à-d que toutes les classes héritent de object, tout ce qui est défini dans cette classe sera donc utilisable dans tous les objets que nous créerons, puisqu'ils en hériteront tous.mieux encore, quand on déclare une classe sans la faire hériter explicitement de Object, le compilateur se charge de le rajouter. Une classe hérite toujours implicitement de Object.
j'ai trouvé une information que je n'ai pas saisie c'est:
"une variable de type Object peut être utilisée pour référencer un objet de type quelconque :
                              Point p = new Point (...) ;
                              Pointcol pc = new Pointcol (...) ;
                              Fleur f = new Fleur (...) ;
                              Object o ; o = p ; OK
                              o = pc ; OK
                              o = f ; OK
Cette particularité peut être utilisée pour manipuler des objets dont on ne connaît pas le type exact (au moins à un certain moment). Cela pourrait être le cas d'une méthode qui se contente de transmettre à une autre méthode une référence qu'elle a reçu en argument. Bien entendu, dès qu'on souhaitera appliquer une méthode précise à un objet référencé par une variable de type Object, il faudra obligatoirement effectuer une conversion appropriée. Voyez cet exemple où l'on suppose que la classe Point dispose de la méthode déplace

                                 Point p = new Point (...) ;
                                      Object o ;
                                        ...
                                o = p ; o.déplace() ; erreur de compilation
                                ((Point)o).déplace() ; OK en compilation (attention aux parenthèses)
                                 Point p1 = (Point) o ; OK : idem ci-dessus, avec création d'une référence
                                 p1.déplace() ; intermédiaire dans p1    "

Voila, et surtout ce dernier exemple qui ma posé de problèmes!!!
une autre phrase qui dise
"Tous les objets, incluant les tableaux, implémente les méthodes de la classe object.
Tout type tableau dérive également de Object. Par suite, toute méthode déclarée avec un paramètre de type Object accepte en argument un tableau quelconque ou un objet de n'importe quelle classe, mais pas une valeur d'un type primitif."

vous pouvez me donner une explication plus claire?
et merci pour tout





Commentaire de pouicky le 24/07/2007 01:20:37

Je viens de faire une petite source simple avec des animaux illustrant l'évolution du code en partant d'un code linéaire et redondant pour le corriger avec l'abstraction puis la décoration.
on peut le voir ici:
http://www.javafr.com/code.aspx?ID=43554

Pour ce qui est du rapport entre la classe Objetc et les classes abstraites je ne vois pas trop. Peut etre est-ce que la classe Object est une abstraction de toutes les autres sur un plan conceptuel?

Commentaire de ouafaa83 le 24/07/2007 03:20:07

je dirais plutot que la classe Object est une classe abstraite! puisqu'on ne peut pas l'instancier, mais je ne vois pas la relation entre les classes abstraites et des enveloppes, je peut vous dire que les classes enveloppes Byte, Short, Integer, Long, Float et Double sont tous des déscendants de la classe Number qui est une classe abstraite...

Commentaire de pouicky le 24/07/2007 12:11:26

//on peut instancier un objet de classe Object :
public class Lanceur {
  public Lanceur() {
  }
  
//Méthode main
  public static void main(String[] args) {
      Object a=new Object();
      System.out.println(a.toString());
  }
}
Alors non je vois toujours pas le probleme que tu as avec la classe object, désolé.

Commentaire de sheorogath le 15/08/2007 00:33:45 administrateur CS

ta pas parler des interfaces ...
sinon c'est pas mal , je n'ai fais que survoler masi ca a l'air bien

Commentaire de pouicky le 17/08/2007 00:09:45

Tu as raison, j'avais promis. Mais les aléas de la vie me tiennent trop souvent éloigné de ce doux loisir qu'est la programmation. Alors dès que je peux, je m'y penche.
   Cependant, au sujet des interfaces, je suis encore innocent et je ne vois pas clairement les cas où elles seront préférées à d'autres outils. J'ai voulu m'y intéresser plus après une phrase de Delphiprog (Codes sources delphi) qui prône la programmation d'interfaces plutôt que le codage de classes. Dans l'état actuel de mes connaissances, il me semble que c'est un outil qui améliore la souplesse d'un code et sa maintenance, sur une durée de vie longue; qui permet d'imposer des contraintes d'implémentation à une équipe de dev mais sur des petits projets perso, quand sont-elles incontournables?
   Merci de tes encouragements. Je suis de mon côté impressionné par ton jeune âge, le nombre et la technicité de tes sources. Chapeau bas. Ne te laisse pas pour autant avaler par ton écran ;)

Commentaire de zarkofaj le 16/10/2007 07:01:58

Excellente explication. Merci bien .

Commentaire de sheorogath le 17/10/2007 00:14:56 administrateur CS

merci pour le compliment , disons que je travail et que pour certain trucs j'ai eut une aide precieuse qui m'a ouvert les yeux sur quelques truc en prog

sur les petits projet elles ne sont pas tres utile c'est vrai mais sur les un peu plus gros ca donne une code plus propres avec des dependances interclasse beaucoup plus claire et mieu gerees

sinon pour l'ecran t'inquiete pas , je continue a essayer de lui imposer ma volonte ^^

bonne soiree++

Commentaire de bernardGigi le 24/01/2008 12:53:49

Excellent, tu peux aussi dire (pour les plus forts) que ton exemple n'est rien d'autre que le design pattern patron de méthode (template method).
Bravo

Commentaire de kriesgraf le 19/02/2009 14:05:08

Je développe pas mal en VB.NET, mais en ce moment je suis obligé de faire un peu de java que je maitrise pas entièrement. Mais la conception objet ça, je maîtrise assez.

Or autant VB.NET est très explicite (donc parfois contraignant). Il stipule exactement si on doit/peut surcharger/implanter une méthode, hériter/ou pas d'une classe.

Mais dans java, je trouve notament bizarre les différences de notations:
- une méthode non implémenté est préfixé "abstract" (ce que vous disez plus haut, moi ça me va !),
- quand on l'implante dans une classe dérivée, la méthode est affublé d'un "@Override", idem si on la surcharge dans une troisième classe dérivée.
- et enfin si on décide de ne pas autoriser une nouvelle surcharge on préfixe la méthode d'un "final".

Ne trouvez vous pas ça bizarre !

Quand pensez-vous ?

Commentaire de pouicky le 19/02/2009 14:43:11

Je ne vois pas d'incohérence.

Je n'ai pas mis les doigts dans .Net depuis que ça existe. Mais j'ai bien compris que c'était un gros frameWork comme JAVA SDK qui permet la programmation objet.

Dans les grand mécanismes utilees à ce paradigme on trouve en java ce qui suit:
Les interfaces qui sont un moyen d'être sûr que des classes les implementant présenteront les méthodes prévues.
Le classes abstraites qui requierent une implémentation de leurs méthodes dans les classes filles.
Elles sont plus riches que des interfaces, puisqu'elles permettent de déclarer des propriétés, d'implémenter des méthodes concrêtes utilisant les méthodes abstraites. Bref c'est un moule très fin pour des classes filles.
La surcharge qui doit être possible aussi en .net. il s'agit de l'implementation de la dernière méthode de même signature appelée dans la dynastie des Classes.

En java comme en .NET apparemment, on peut contraindre ou non l'héritage:
Dans une classe on spécifie le comportement attendu dans la descendance de la classe, c'est à dire qu'on "stipule exactement si on doit/peut surcharger/implanter une méthode, hériter/ou pas d'une classe"
abstract : Cas des classes abstraites : obligation pour les descendants d'implementer
final : interdiction aux descendants de modifier
implements + Interface : obligation pour la classe d'implémenter
rien de spécial : on peut surcharger à volonté.

Remarque:
le symbole @override apparait automatiquement dans eclipse ou autre EDI mais n'est qu'une annotation. Cela ne sert qu'à permettre au compilateur de vérifier pour nous si le code est organisé comme prévu, mais n'est pas nécessaire sauf politique de travail en équipe, ou autre contrainte...

Commentaire de sheorogath le 19/02/2009 17:00:11 administrateur CS

Mouarf, c'est toujours mieux que le CPP qui n'as que virtual :P ou le python ou il n'y a rien

Je trouve les notations java assez clair par rapport a d'autre langage

Commentaire de verdy_p le 26/04/2009 17:32:24

Il aurait peut-être fallu expliquer la différence entre classe abstraite et interface: les classes abstraites restent à héritage simple. Ce qui veut dire que dans leurs parents (classes de bases ou interfaces implémentées) il n'existe qu'un seul chemin donnant accès à une implémentation. Au contraire les interfaces utilisent l'héritage multiple: ce n'est pas un problème car cela ne multiplie pas les implémentations possibles d'une même méthode.
De plus les interfaces ne peuvent avoir de propriétés (elles peuvent en revanche définir des constantes entières ou String (mais qui sont copiées à la compilation dans les classes qui les implémente au lieu d'être référencées à l'exécution ce qui peut poser des problèmes pour l'évolution du logiciel si une constante doit être modifiée car les classes implémentant ces interfaces risquent de continuer à utiliser l'ancienne valeur copiée de l'ancienne définition de l'interface). En pratique, une interface n'est pas faite pour être modifiée: c'est un contrat auquel adhère toutes les autres classes "dérivées" d'elles (on dit "implémenter" en Java).
Au dela de ce vocabulaire, l'analogie avec le C++ est possible, car dans les deux cas, les classes abstraites de Java  ou les interfaces de Java sont des classes (au sens large) déclarant des méthodes (le contrat) que les classes filles doivent implémenter.

Pourquoi alors utiliser des interfaces en Java et pas des classes abstraites ?
- les interfaces ont une syntaxe bien plus légère: toutes les méthodes déclarées sont implicitement abstraites (toutes les méthodes d'une interface DOIVENT être déclarées dans les classes "filles" implémentant l'interface)
- les interfaces permettent l'héritage multiple (c'est au prix qu'aucune méthode n'ait une implémentation de leur code)
Pourquoi utiliser des classes abstraites et pas des interfaces en Java ?
- une classe abstraite n'oblige pas toutes ses méthodes à être abstraites, il est possible d'y mettre du code (quitte à ce que ce soit seulement du code par défaut, que la classe fille peut surcharger pour le changer).
- une classe abstraite peut avoir des membres statiques, des initialiseurs (mais une interface ne peut en avoir)

Bref une classe abstraite est presque une classe complète, la différence étant qu'on ne peut pas l'instancier, elle n'a donc pas de constructeur utilisable directement et on ne peut pas utiliser "new" pour en créer une instance. Pourtant elle peut avoir un constructeur ! (qui ne sera utilisé qu'à travers les constructeurs des classes filles).

En Java, chaque fois que possible, il faut tenter d'utiliser les interfaces plutôt que les classes abstraites: cela donne nettement plus de flexibilité, et c'est bien plus lisible et structurant. Il n'y a que de rares cas où des classes abstraites sont nécessaires (pour donner un comportement par défaut aux classes implémentant une interface, penser au paradigme des factories. L'interface donne une très bone isolation (la boîte noire) permettant de séparer les couches logicielles, elle limite les interactions et dépendances de code, elle facilite la substitution du code d'une classe par un autre sans trop affecter la hiérarchie descendante.

L'équivalent en C++ d'une interface en Java, c'est une classe virtuelle pure, dont toutes les méthodes sont virtuelles pures, et qui n'a aucun champ membre (uniquement des méthodes, ou des constantes statiques ne nécessitant aucun initialiseur). En C++ comme en Java, on peut mettre des propriétés dans ces interfaces ou classes virtuelles pures (les propriétés sont des accesseurs particuliers de type get/set, ce sont donc aussi des méthodes, pas des champs membres).

Commentaire de verdy_p le 26/04/2009 19:11:08

Tout ce que je viens de dire signifie que parler de classe abstraite tout de suite n'est pas la bonne méthode de travail pour la modélisation commentée ci-dessus. Mon code aurait plutôt été en faveur de l'interface:

public interface AnimalCapable {
   void obtenirNourriture(); //points d'entrée pour le code des classes enfants
   void ingurgiter();
   void digérer();
}

qui définit (de façon bien plus légère) ce que tout animal devrait savoir faire. Note: il est d'usage en Java de nommer les interfaces avec un suffixe en "able" (Sortable, Comparable, Iterable...), alors qu'en .Net c'est plutôt un préfixe "I". Ce n'est pas une obligation absolue, mais ça clarifie le code.

(pas la peine d'indiquer abstract ni public dans les méthodes déclarées dans les interfaces. On pourra créer ensuite autant d'espèces animales que nécessaire en n'implémentant que ces méthodes pour chacun:

public class Chat implements AnimalCapable {
   public Chat() {
      //...
   }
   public void obtenirNourriture() {
      seRéveiller();
      miaulerPourSortir();
      courirSurLesToits();
      fouillerLesPoubelles();
      dénicherUneSouris();
      jouerAvecLaSouris();
      laSaisirAvecLesGriffes();
      laTuerDunCoupDeDents();
      retournerDormirAvecLaSouris();
   }
   void ingurgiter() {
      croquerLaTeteDeSouris();
      couperlaQueue();
      mangerLesPattes();
      laisserLEstomacSurPlace();
   }
   void digérer() {
      chercherUnPlacard();
      glisserSousLeLinge();
      seRoulerEnBoule();
      dormirUneHeureOuDeux();
      chercherSaCaisse();
      faireSesBesoins();
      recouvrirEtEffacerSesTraces();
      marquerSonTerritoire();
   }
}

On refera ça avec les chiens, les lions, ou même avec la souris, avec le code spécifique à chaque espèce.

Ensuite on peut déclarer une classe ajoutant des méthodes non déclarées dans l'interface, et qu'il n'est pas nécessaire d'écrire dans le code de chacun des animaux. Cette classe générique sera nécessairement abstraite car les autres méthodes ne sont pas implémentées ici mais dans chaque animal.

public abstract class Animal implements Animalcapable {
    // il est nécessaire de déclarer les méthodes issues de l'interface AnimalCapable.
    // Ce ne devrait pas être obligatoire dans une classe abstraite (contrairement aux concrètes),
    // la déclaration de l'interface implémentée devant suffire, mais Java l'exige:
    public abstract void obtenirNourriture();
    public abstract void ingurgiter();
    public abstract void digérer();

    public voidsAlimenter(){
        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
   }
}

Une fois qu'on a fait ça, comment peut-on faire que nos chats et nos chiens puissent utiliser cette nouvelle méthode? Si on a le source des chiens et chats, il serait simple de faire qu'ils de cette classe abstraite Animal héritent ("extends Animal"). Cependant ce n'est pas toujours possible, parce que ces objets peuvent avoir hérité d'une autre classe ou bien le source n'est pas disponible

C'est là qu'interviennent les "wrappers" ou classes enveloppes, et les factories. commençons par les wrappers (un par espèce animale):

public class ChatAnimal extends Animal {
   private Chat a;
   public ChatAnimal()             { a = new Chat(); }
   public void obtenirNourriture() { a.obtenirNourriture() }
   public void ingurgiter();       { a.ingurgiter(); }
   public void digérer();          { a.digerer(); }
}

On notera que le code de ce "wrapper" (classe enveloppe) est simplissime: on délègue à l'instance interne de Chat (cachée à l'intérieur, on dit "emballée", "enveloppée") toutes les méthodes nécessaires. On note aussi que (bien que ce ne soit pas indiqué ici, la classe ChatAnimal implémente aussi l'interface AnimalCapable dont elle hérite simplement via la classe abstraite Animal qu'elle a étendue). Il s'agit toujours du même chat qu'à l'origine, la classe Chat n'a pas à être modifiée. On dit que cette classe enveloppe "adapte" une autre. On parle parfois aussi d'"Adapter" en Java (comme en C++).

Les wrappers permettent de passer outre les "limitations" liées à l'héritage simple, mais par une délégation explicite (donc claire) contrairement à l'héritage multiple (de C++) où la délégation de l'implémentation est à chercher dans un graphe compliqué d'héritages avec une règle unique et non adaptable. On constante que cette délégation reste entièrement sous le contrôle du code qui peut prévoir des exceptions, ou prévoir des délégations multiples ou conditionnelles.

Il ne reste alors qu'à faire une fabrique (un "factory") pour nos nouveaux animaux. Par exemple (code simplifié, les factories peuvent connaitre la liste des animaux de diverses façons, y compris par Reflection; pour l'instant on se contente de retourner ici une exception pour les espèces inconnues).

public class AnimalFactory {
   public static Animal getInstance(String espece) {
      if (espece.equals("Chat"))
          return new ChatAnimal();
      if (espece.equals("Chien"))
          return new ChienAnimal();
      //...
      throw new RuntimeException("espèce '"+espece+"' inconnue");
   }
}

La même fabrique (factory) aurait pu faire des "new Chat()" ou "new Chien()" mais les instances retournées n'auraient pas eu le comportement ajouté dans la classe abstraite Animal, et donc n'aurait pu retourner que des instances "AnimalCapable" et non des instances "Animal" complètes.

Ce qui veut dire que pour les alimenter(), il faudrait répéter à chaque fois le code appelant les méthodes obtenirNourriture(), etc... dans le même ordre que prévu. Ce qui va contre le principe de réutilisation du code.

Là aussi la fabrique dissimule le fait qu'on est passé des Chat aux ChatAnimal. La fabrique sert donc aussi d'adaptateur. De l'extérieur, la fabrique dissimule aussi la liste des spécialisations qu'elle sait gérer. On pourra l'utiliser de la même façon pour obtenir et manipuler des chats, des chiens... ou toute autre espèce qu'elle connait.

Pourquoi tout ces concepts alors s'il était si simple de modifier les classes de base? D'abord c'est en complexifiant les classes de base qu'on augmente la complexité de leur conception. Ces classes de base deviennent longues à concevoir, puis rapidement inefficaces ou ont à gérer trop de cas et on a des bogues sur des tas de cas non testés ou oubliés. Les adaptateurs simplifient largement le problème en adaptant juste ce qui est nécessaire pour gérer un problème particulier.

Et là, les outils proposés en Java permettent de résoudre ces problèmes de façon simple, tout en conservant un typage fort et une grande clarté du code source, et en permettant de conserver la séparation du développement du code (demandez-vous les ocmpétences qu'il faut pour développer la classe Chat ci-dessus, si vous n'êtes pas expert des félins mais seulement des chiens), comparez à celle demandée pour développer la classe Animal. Chacun connait son domaine et fait confiance (délègue) ce qu'il ne sait pas faire à quelqu'un d'autre qui développe sa propre partie du code, tout en ayant le minimum de choses à tester à son propre niveau.

Commentaire de verdy_p le 26/04/2009 19:54:56

Parmi les autres "wrappers" (classes enveloppes) on trouve aussi en Java:
- les "box" qui servent à emballerles types primitifs dans un objet. Depuis Java 1.5 l'autoboxing est géré nativement (il était déjà nécessaire pour certaines opérations notamment via Reflection): le type primitif est un membre privé de la classe qui fournit un constructeur (éventuellement aussi une méthode statique de factory). Exemple: la classe "java.lang.Integer" est un wrapper pour "int" (qui n'est pas un objet et donc pas une référence, mais que java.lang.Integer vient se faire comporter comme un objet référençable).
- les classes "proxy" qui servent à créer des instances locales d'objets dont le comportement est réellement déterminé par celui d'un autre non nécessairement sur la même machine (les deux objets vivant simultanément sur chaque système via un canal de communication interne privé, mais dont le paramétrage pour la connexion peut être fourni à une méthode de factory: le code réel de la méthode n'est pas dans la classe proxy mais dans la classe distante, la classe proxy se contente d'emballer les paramètres nécessaires au pilotage de l'objet distant. Certains objets distants peuvent être référencés par plusieurs proxies distincts, l'objet distant gère les droits d'accès et le partage;, ou bien un mode transactionnel.
- des classes de persistance (vers un fichier, une base de données...): similaire aux objets proxy, sauf que l'objet distant peut perdurer sous sa forme propre, même après la fin de vie du proxy qui pourra être recréé plus tard. Ce type d'objet quand c'est un wrapper, masque la localisation réelle de l'objet (en mémoire ou ailleurs) et sert habituellement aussi à gérer des caches de données, afin de minimiser les échanges de données ou les entrées-sorties liées à la persistence.

Les wrappers n'ont rien à voir avec les classes abstraites, même si leur comportement peut sembler abstrait (sortes de boites noires). Il n'est en effet pas du tout nécessaire qu'un wrapper soit une classe abstraite, et même dans le cas général les wrappers sont pratiquement toujours concrets: "java.lang.Integer" est une classe bien concrète pour le type tout aussi concret "int" et on peut aussi bien faire un "new Integer()", alors qu'on ne peut pas faire un "new Object()"...

Les classes abstraites sont en revanche des briques intermédiaires de construction, à mi-chemin entre une interface et une classe concrète, mais jamais utilisables directement. On les conçoit afin de faciliter l'implémentation d'une famille de classes concrètes devant partager du code commun, en même temps que cette famille (alors que l'interface peut être développée séparément). On l'a vu plus haut, les classes abstraites peuvent cependant simplifier énormément le développement des classes concrètes de wrappers, en évitant d'avoir à y dupliquer trop de code dans chacune d'elles.

Commentaire de pouicky le 26/04/2009 20:35:22

Grand merci à toi Verdy_P.
Je t'avouerais que j'attendais un peu de réactions pour donner un peu plus de ce que j'ai appris entre temps sur la conception objet, et les patterns.
Mais tu as de manière très complète couvert le sujet sur l'intérêt des interfaces et des classes abstraites en corrigeant au passage quelques approximations hatives de ma part ("new Oject()" ;-)
Tout ceci risque cependant de finir perdu en bas d'un tuto alors qu'on n'est pas loin d'un tuto à part entière.
La difficulté à lever reste que l'évolution des langages va vers plus d'abstraction des mécanismes et donc une progression du plus "concret" au plus "abstrait"(...->procédures et données->classes->classes abstraites->interfaces->...) alors que la conception logicielle suit le chemin inverse (...->Interfaces->classes abstraites->classes->méthodes et champs...)
Pour un néophyte, qui commence par le début ("hello Word"), le concept de classe est appréhendé en premier, et l'intérêt des concepts plus abstraits ne peut être découvert qu'à l'occasion de projets conséquents en complexité ou en organisation.
Tout l'intérêt ici était donc pour moi de rattacher la notion de classe abstraite à celle plus familière de classe(concrête), en la motivant par le besoin de factoriser le code commun aux descendantes.
Encore merci pour le temps consacré à ces éclairages.

Commentaire de 3filIar le 09/10/2009 02:42:37

slt Mr pouicky stp comme déclarer (plusieur déclaration dons cell function (java))
ex:<script language="javascript">
function N1(a,b)
{
var s=parseInt(a)+parseInt(b)
document.calcule.chr.value=s
}
function N2(a,b)
{
var d=parseInt(a)*parseInt(b)
document.calcule.chr.value=d
}
function N3(a,b)
{
var d=parseInt(a)-parseInt(b)
document.calcule.chr.value=d
}

</script>

je veux faire un raccourcie dans cell function et merci

Commentaire de pouicky le 09/10/2009 22:42:52

à 3Filiar:
Quand tu auras un peu bossé, cherché, appris à t'exprimer de manière intelligible, tu comprendras un truc qui ne te fera pas plaisir sur ce que peut ressentir quelqu'un qui a donné un peu de son temps pour partager un peu de ce qu'il a compris (seul en cherchant), lorsqu'il lit ton message mal placé, voire déplacé...
Prends la peine de savoir où tu te trouves, de quoi tu parles et comment en parler. Ensuite tu auras le droit de ne pas tout connaître.

Commentaire de melissasss le 17/02/2010 22:28:59

Bonsoir;
Merci POUICKY;
j'ai adoré votre façon d'expliquer car je suis nouvellement en java;surtout pour l'explication des class abstracts et les interfaces;
je cherche à comprendre la logique generale de java;

par exemple la methode tostring() qui sera redefinie a chque besoin; aussi equal....qui dites des methodes de la class Object; qu'est ce qu'il ya derière cette façon de faire;car en redifinissant equal ou compare..en garde que la semantique et tous le corps à refaire...j'ai pas arriver à comprendre cette logique de faire;
merci .

 Ajouter un commentaire




Nos sponsors


Sondage...

Comparez les prix

CalendriCode

Février 2012
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
272829    

Consulter la suite du CalendriCode

 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 1,357 sec (3)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales