350 likes | 557 Vues
Programmation Objet en JAVA Cours 2 : Réutilisation, Héritage 1 Agrégation, composition, héritage, Hiérarchie, Constructeurs, Transtypage. Réutilisation ?. Comment utiliser une classe comme matériau pour concevoir une autre classe répondant à de nouveaux besoins ?
E N D
Programmation Objet en JAVACours 2 : Réutilisation, Héritage 1Agrégation, composition, héritage, Hiérarchie, Constructeurs, Transtypage
Réutilisation ? • Comment utiliser une classe comme matériau pour concevoir une autre classe répondant à de nouveaux besoins ? • Quels sont les attentes de cette nouvelle classe ? • besoin des «services» d'une classe existante (les données structurées, les méthodes, les 2). • faire évoluer une classe existante (spécialiser, ajouter des fonctionnalités, ... ) • Quelle relation existe entre les 2 classes ? • Dans une conception objet, des relations sont définies, des règles d'association et de relation existent. • Un objet fait appel un autre objet. • Un objet est crée à partir d'un autre : il hérite.
La relation client/serveur - Agrégation • Un objet o1 de la classe C1 utilise un objet o2 de la classe C2 via son interface (attributs, méthodes). • o1 délègue une partie de son activité et de son information à o2. • o2a été construit et existe par ailleurs. • Faire une référence dans C1 vers un objet de C2; • Attention o2 est autonome, indépendant de o1, il peut être partagé. • C'est une agrégation. Le client Le serveur public class C1 { private C2 o2; ... } public class C2 { ... ... }
La relation client/serveur - Agrégation Le client Le serveur public class Cercle { private Point centre; ... } public class Point { ... ... }
La relation client/serveur - Composition • Mais si o2 évolue, est-ce grave? • Comment faire pour que o1 possède son o2 à lui. • La solution se trouve dans le constructeur de C1 : doter o1 d'un objet o2 qui lui appartienne. • C'est une composition. Le client Le serveur public class C1 { private C2 o2; ... C1(...){ o2 = new C2(...); } ... } public class C2 { ... ... }
La relation client/serveur - Composition Le client Le serveur public class Cercle { private Point centre; ... public Cercle(...){ centre = new Point(...); } ... } public class Point { ... ... }
L'exemple du cercle • Le cercle c1 a un centre. C'est le point p1. • Utilisation d'un objet de type Point, car la classe existe déjà. • Si agrégation, tout changement effectué sur p1 a des conséquences sur c1. • Si on déplace p1, on déplace tous les cercle ayant comme centre p1. Composition ? • Mais plusieurs cercle peuvent partager le même centre ... Impossible si composition. • ça se discute ! • Question : A-t-on besoin du code source de la classe Point pour coder la classe Cercle ? • Besoin d’un objet existant ? « Se connecter à un serveur » • Confiance en l’encapsulation Agrégation • Composition et getter.
Nouveau problème • Si le serveur est insuffisant, incomplet, inadapté ? • un objet Point répond à un besoin «mathématique». • Si on veut en faire un objet graphique, avec une couleur et qu'il soit capable de se dessiner ? • Mais en gardant les propriétés et les fonctionnalités d'un Point. • La solution en POO : l'héritage. • Définir une nouvelle classe à partir de la définition d'une classe existante. • Spécialiser, augmenter.
Héritage public class LaClasseFille extends LaClasseMere{...} class PointAvecUneCouleur extends Point • En java, on n'hérite que d'une seule classe. • Un objet de ClasseFille peut faire tout ce que sait faire un objet de ClasseMere • Il le fait différemment et/ou il peut plus.
Hiérarchie en Héritage • L'héritage est une notion transitive • Hiérarchie d'héritage : l'ensemble des classes dérivées d'un parent commun. • Attention ce n'est pas bijectif : • un Point3D est un Point. • Le contraire est faux. Chaîne d'héritage : le chemin menant d'une classe vers ses ancêtres.
Concept d’héritage • Concept fondamental des langages objet. • Dériver une nouvelle classe à partir d'une classe existante en récupérant ses propriétés. • Avantage • Réutilisation : factorisation, enrichissement de champs et méthodes de classes existantes. • Spécialisation d’une classe existante sans la modifier. • Pas de limitation en profondeur. • Contrainte • Héritage simple : une classe ne peut hériter que d'une seule classe. • Toutes les méthodes et champs ne sont plus regroupées dans le même fichier
Exemple d'héritage import Point; public classPointAvecUneCouleurextendsPoint { int couleur, x, y; // x et y sont hérités public PointAvecUneCouleur(int couleur) // un constructeur { super(); // appel au constructeur de la classe mère setCouleur(couleur); } public PointAvecUneCouleur(int x, int y, int couleur) // un constructeur { super(x,y); // appel au constructeur de la classe mère setCouleur(couleur); } public void setCouleur(int couleur) { this.couleur=couleur; // désigne l'objet encours }
La classe "Object" • Toutes les classes Java sont des descendants de la classe Object. • Object est parent par défaut des classes qui ne précisent pas leur ancêtres • Object offre quelques services génériques dont la pluspart sont à refaire, ... à redéfinir (attention au fonctionnement par défaut) : public final Class getClass(); • Renvoie un objet de type Class qui permet de connaître le nom d'une classe public boolean equals(Object o); • Compare les champs un par un (pas les pointeurs mémoire comme ==) protected Object clone(); • Allocation de mémoire pour une nouvelle instance d'un objet et recopie de son contenu public String toString(); • Renvoie une chaîne décrivant la valeur de l'objet
La classe "Class" • C'est la classe qui identifie les types à l'exécution • Permet la sélection des méthodes en fonction de la classe lors des appels public String getName(); • Renvoie le nom d'une classe public static Class forName(String s); • La fonction réciproque de getName public Object newInstance(); • Crée une nouvelle instance de même type public String toString(); • Renvoie une chaîne décrivant la valeur de l'objet
La méthode equals() • Egalité entre objet et non entre référence • Très utile pour les String • If (myString == "Hello") {…} Toujours Faux !!! • If (myString.equals("Hello") ) {…} OK • Si les 2 objets o1 et o2 ont des références r1 et r2 comme champs, il faut que r1==r2 pour que o1.equals(o2) et non pas que r1.equals(r2) • Réflexive, symétrique, transitive et consistent • Pour les classes que vous créez, vous êtes responsable. Point p1 = new Point(2,1); Point p2 = new Point(2,1); if (p1==p2){...} // Ici c'est faux if (p1.equals(p2)){...}// OK si equals est bien redéfinit p1 = p2 // Co-référence if (p1==p2){...} // Vrai désormais
Les fonctions hashCode() et toString() • hashCode() renvois un chiffre différent pour chaque instance différente • Si o1.equals(o2) alors o1.hashCode()==o2.hashCode() • Le contraire n’est pas assuré mais préférable (@mémoire) • Très utile pour les HashTable. • toString() conversion en String d'un objet : String s = '' mon objet visible '' + monObjet; String s = '' mon objet visible '' + monObjet.toString();
Constructeurs et héritage • L'héritage permet la réutilisation de code. Le constructeur est concerné. • Possibilité d'invoquer une méthode héritée : la référence super. • Invocation d'un constructeur de la super-classe : • super(<params du constructeur>) • L'utilisation de super ressemble à celle de this. • L'appel à un constructeur de la super classe doit toujours être la première instruction dans le corps du constructeur. • Si ce n'est pas fait, il y un appel implicite : super(). • Lors de la création d'un objet, les constructeurs sont invoqués en remontant de classe en classe la hiérarchie, jusqu'à la classe Object. • Attention, l'exécution se fait alors en redescendant dans la hiérarchie. • Un constructeur d'une classe est toujours appelé lorsqu'une instance de l'une de ses sous classes est créée.
Héritage et constructeurs - suite public class Point extends Object { public Point(double i, double j) {...} } public class Object { public Object() {...} } public class PointAvecUneCouleur extends Point { public PointAvecUneCouleur(int i, int j, int c){ super(i,j); couleur=c; }
Retour sur protected • Dans la classe Point pour setX on a 3 possibilité : • public : un objet PointAvecUneCouleur peut fixer l'abscisse, ... mais aussi tout le monde. • private : plus personne ne peut, y compris un objet PointAvecUneCouleur. • protected : la solution ici, les objets d'une classe héritière ont accès aux membres protected (+friendly). public void setX (double nouveauX){...} private void setX (double nouveauX){...} protected void setX (double nouveauX){...}
Transtypage et héritage : surclassement. • Le transtypage consiste à convertir un objet d'une classe en objet d'une autre classe • Vers une «super-class» (upcast ou surclassement), c'est toujours possible : on peut toujours transtyper vers une super-classe, plus pauvre en informations. Point3D unPoint3D = new Point3D( x, y, z ); Point unPoint = (Point) unPoint3D;// une projection sur xOy • La mention du transtypage est facultative, mais recommandée pour la lisibilité. • « un Point3D est un Point mais pas le contraire. » • Un message envoyable à un objet, l'est aussi pour tous les objets d'une classe héritière : PointAvecUneCouleur p = new PointAvecUneCouleur(); p.translater(1,3); // Méthode définit au niveau de Point
Transtypage et héritage : surclassement. • Le transtypage consiste à convertir un objet d'une classe en objet d'une autre classe • Vers une «super-class» (upcast ou surclassement), c'est toujours possible : on peut toujours transtyper vers une super-classe, plus pauvre en informations. Point3D unPoint3D = new Point3D( x, y, z ); Point unPoint = (Point) unPoint3D;// une projection sur xOy • La mention du transtypage est facultative, mais recommandée pour la lisibilité. • Attention : unPoint3D est de type Point3D mais aussi Point. • « un Point3D est un Point mais pas le contraire. » • Un message envoyable à un objet, l'est aussi pour tous les objets d'une classe héritière : PointAvecUneCouleur p = new PointAvecUneCouleur(); p.translater(1,3); // Méthode définit au niveau de Point
Transtypage et héritage : sous-classement • Le comportement d'un objet «hérité» est augmenté par rapport à un objet d'une classe mère. • Le transtypage vers une sous-classe (downcast ou sous-classement) • Impossible (sauf si un surclassement avait été fait) • pour transtyper vers une sous-classe, utiliser le mot-clé réservé instanceof. unPoint = new Point3D(...); .... if( unPoint instanceof Point3D) { unPoint3D = (Point3D) unPoint; unPoint3D.projection3D(...); } • La mention du transtypage est obligatoire, dans ce cas.
Un exemple : la banquise • Un plateau composé de 8x8 cases • Les 4 coins : terre ferme • les autres cases : banquise. Lorsqu’un joueur fait fondre une case banquise, les propriétés se révèlent : • certaines cases ne fondent pas et fait apparaître un renard • d’autres fondent font apparaître de l’eau et un banc de poissons ou ours • d’autres donnent au joueur des capacités (elles sont donc garder par le joueur) • d’autres implique une action immédiate (dérive) • Pour résumer : deux grande familles de Cases, action et modification du joueur. • Sur une case, peuvent être posés des pions et des igloos.
banquise : questions • Comment encoder la notion de plateau ? • Comment encoder la notion de pion, igloo et des différentes actions possibles ? • Pour les cases : • Un type d’objet (sa classe) se définit par sa structure de données et son comportement. • Si deux objets ont des comportements différents, ils appartiennent à deux types différents -> des classes différentes. • Ce qui est générique à tous : peut contenir des pions et igloos • Il y a plusieurs types de banquises, toutes ont une action lors de la fonte.
banquise : conception Case // Contient Pions et Igloos sous forme de vecteurs | // -> agrégation ou composition ? | |--- BanquiseFerme // la tuile change de forme, mais il ne | // se passe rien. |--- BanquiseAction // la fonte déclenche une action | // du joueur -> lui envoie un | // message spécifique |--- BanquiseCapacité // la fonte modifie le joueur // -> lui envoie un message spécifique • Toutes les banquises sont des cases. • Toutes les cases banquises ont une méthode action. • Cette méthode est différente selon le type de la case (sa classe). • Le plateau est un tableau de Cases
Référence sur une Case @0ffa88 Objet Case en mémoire Vector<Pion> pions; Igloo igloo; ... Vector<pion> getPions(); boolean addPion(); boolean addIgloo(); boolean equals(Object o); String toString(); macase Case macase = new Case ( ... );
Référence sur une BanquiseFerme @0ffa98 Objet BanquiseFerme en mémoire public BanquiseFerme extends Case Vector<Pion> pions; Igloo igloo; ... BanquiseFerme macase = new BanquiseFerme(...); Vector<pion> getPions(); boolean addPion(); boolean addIgloo(); boolean equals(Object o); String toString(); macase void action( ... );
Surclassement d’une BanquiseFerme @0ffa98 Objet BanquiseFerme en mémoire public BanquiseFerme extends Case Vector<Pion> pions; Igloo igloo; Case macase = new BanquiseFerme(null,1); Vector<pion> getPions(); boolean addPion(); boolean addIgloo(); boolean equals(Object o); String toString(); Code hérité de la classe Case macase void action( ... );
Surcharge et redéfinition (overload vs override) public class Point { ... public void maMethode(int i){...} } Surcharge Redéfinition public class Point3D { ... public void maMethode(double i){...} } public class PointCouleur { ... public void maMethode(int i){...} } Pour un objet Point3D : • 2 méthodes maMethode Pour un objet PointCouleur : • une seule maMethode
macase Redéfinition et surclassement public Case { ... public String toString(){ return « TerreFerme »; } } public BanquiseFerme extends Case{ ... public String toString(){ return « BanquiseFerme »; } } @0ffa98 Objet BanquiseFerme en mémoire Vector<Pion> pions; Igloo igloo; Vector<pion> getPions(); boolean addPion(); boolean addIgloo(); Code hérité de la classe Case String toString(); Case macase = new BanquiseFerme(...); System.out.println(macase); void action();
Exemple animalier manger dormir marcher courir (2 x marcher) espèce Animal Tortue Cheval Ours courir galoper hiberner Cheval de traie Cheval de course galoper galoper
Blocage de l'héritage • Mot-clé final • Le mot-clé final devant une classe indique qu'elle ne peut avoir de descendance. final class Point3D { ... } • Avantage • Efficacité : implémentation statique possible. • Sécurité : pas de redéfinition possible des méthodes pour une descendance. • Inconvénients • Impossibilité de sous-classer pour créer une classe de test indépendante
Conseils de conception • Faire des schémas • Représentant les classes et leur hierarchie • Pour comprendre les interactions entre les classes (qui a besoin d’une référence sur qui) • Pour savoir ou placer une fonction • Pour bien penser les droits d'accès • Représentant les appels de fonction • Pour savoir quels objets sont traversés • Pour savoir où récupérer les données à mettre en argument • Attention l'héritage n'est pas une réponse à tout, la délégation est aussi utile. • l'héritage est une relation is-like.