programmation objet (en java)denoyer/courses/2006-2007/li314...leur(s) super-classe(s) ex. rectangle...
TRANSCRIPT
Programmation Objet (en Java)
UPMC – LicenceInformatique - LI 314
© 2006-2007 Frédéric PeschanskiLaboratoire d'Informatique de Paris 6
email: [email protected]
Plan du courspremière saison
Encapsulation et protection de l'information (cours 1 et 2)
Héritage et polymorphisme (cours 3 et 4)
Modélisation objet
Compilation, dispatch dynamique, clonage, etc.
Interfaces et paquetages (cours 5)
+ «tout» sur final et static
Plan du coursInterfaces et Paquetages
Interfaces
Du concret à l'abstrait
A quoi servent les interfaces ?
Paquetages et modularités
Point de vue paquetage
Glossaire O.O Java
Tous les mots-clés « orientés-objets » de la saison 1
+ «tout» sur final et static
I) Du concret vers l'abstrait
abstrait
concret
●Classes abstraites «pures»
●Classes abstraites
●Classes concrètes
●Interfaces
Classe concrète
Déclare (spécification)un type: le nom de la classe (+ lien d'héritage)Type et nom des attributsSignatures des méthodes (+ méthodes héritées)Accès: public, private, protected ou /* package */
Définit (implémentation)code associé à chaque (signature de) méthode
Niveau d'abstraction: 0% abstraitpour représenter/implémenter des concepts concrets
ex. Point, Voiture, Compte (banque), etc.
Exemple: la classe Point
public class Point extends Figure {private double x;private double y;
public Point(String nom,double x,double y) {super(nom);this.x=x;this.y=y;
}
public String getX() { return x; }
public String getY() { return y; }
public void translater(double tx,double ty) {x+=tx;y+=ty;}
public String toString() {return super.toString()+":("+x+","+y+")";
}}
public class Point extends Figure {private double x;private double y;
public Point(String nom,double x,double y) {super(nom);this.x=x;this.y=y;
}
public String getX() { return x; }
public String getY() { return y; }
public void translater(double tx,double ty) {x+=tx;y+=ty;}
public String toString() {return super.toString()+":("+x+","+y+")";
}}
Point
+ Point(x : double, y : double)+ getX() : double+ getY() : double+ translater(tx : double, ty : double) : void+ toString() : String
hérite de la classe Figure (cf. plus loin)
Représentation UML:vision client
Classe concrète: Possibilités
Utiliser:Déclarer des variables du type de la classeConstruire des instances de la classe (new)Invoquer des méthodes déclarées sur les instances de la classe
Hériter:Définir des sous-classes également concrètes
Classe abstraiteDéclare (spécification)
un type: le nom de la classe (+ lien d'héritage abstrait)Type et nom des attributsSignatures des méthodes (+ méthodes héritées)
méthodes concrètes (avec implémentation)méthodes abstraites (sans implémentation)
Accès: public, private, protected ou /* package */
Définit (implémentation)code associé à chaque méthode concrète
Exemple: la classe Figure
public abstract class Figure {private String nom;
protected Figure(String nom) {this.nom=nom;
}
public String getNom() { return nom;
}
public abstract void translater(double tx,double ty);
public String toString() {return nom;
}}
public abstract class Figure {private String nom;
protected Figure(String nom) {this.nom=nom;
}
public String getNom() { return nom;
}
public abstract void translater(double tx,double ty);
public String toString() {return nom;
}}
Représentation UML (point de vue client)
Figure
+ getNom() : String+ translater(tx: double, ty : double) : void+ toString() : String
« abstract »
Classe abstraites: Possibilités
Utiliser:Déclarer des variables du type de la classeConstruire des instances de la classe (new)Invoquer des méthodes déclarées sur les instances de sous-classes concrètes
Hériter:Définir des sous-classes
sous-classes abstraitessous-classes concrètes
Important: nécessité d'implémenter toutes les méthodes abstraites
Cas particulier:classe abstraite «pure»
Déclare (spécification)un type: le nom de la classe (+ lien d'héritage abstrait)pas d'attributSignatures des méthodes (+ méthodes héritées)
méthodes abstraites uniquement (sans implémentation)
Accès: public, private, protected ou /* package */
Ne définit pas d'implémentationNiveau d'abstraction: 100% abstrait
concepts 100% abstraitex. Catégorie («abstract nonsense») en théorie des catégories
Interfaces
Déclare (spécification)un type: le nom de l'interface (+ héritage d'interface)pas d'attributSignatures des méthodes (+ signatures héritées)
Important: signatures uniquement
Accès: public uniquement
Ne définit pas d'implémentationNiveau d'abstraction: 100% abstrait
proche classe abstraite « pure » mais accès uniquement public et héritage uniquement dans les interfaces
Exemple: l'interface Mesurable
public interface Mesurable {public double calculeSurface();public double calculePourtour();
}
public interface Mesurable {public double calculeSurface();public double calculePourtour();
}
Représentation UML (point de vue client uniquement)
Mesurable+ calculeSurface() : double+ calculePourtour() : double
Remarques: ● pas de place pour des attributs● tout en italique (100% abstrait)● accès public (+) obligatoire
«interface»
Interfaces: Possibilités
Utiliser:Déclarer des variables du type de l'interfaceInvoquer des méthodes déclarées sur des instances de classes qui implémentent l'interface
Implémenter: implementsDéfinir des classes «compatibles» avec l'interface
Remarque: le type de la classe devient sous-type de l'interface (à retenir: interface = type)
Hériter: (héritage d'interface)Définir des sous-interfaces héritant d'une ou de plusieurs super-interface(s)Très intéressant mais peu étudié en POBJ (cf. M1)
Exemple: la classe Rectangle mesurable
public class Rectangle extends Figure implements Mesurable {private double x,y;private double l,h;
public Rectangle(String nom,double x,double y,double l,double h) {super(nom);this.x=x;this.y=y;this.l=l;this.h=h;
}
public String getX() { return x; }
public String getY() { return y; }
public String getLongueur() { return l; }
public String getHauteur() { return h; }
public void translater(double tx,double ty) {x+=tx;y+=ty;
}// Ca continue ...
public class Rectangle extends Figure implements Mesurable {private double x,y;private double l,h;
public Rectangle(String nom,double x,double y,double l,double h) {super(nom);this.x=x;this.y=y;this.l=l;this.h=h;
}
public String getX() { return x; }
public String getY() { return y; }
public String getLongueur() { return l; }
public String getHauteur() { return h; }
public void translater(double tx,double ty) {x+=tx;y+=ty;
}// Ca continue ...
Exemple: la classe Rectangle (suite)// La suite ...
// Implémentation OBLIGATOIRE des méthodes de// l'interface implémentée
public double calculeSurface() { return l*h;
}
public double calculePourtour() { return 2*(l+h);
}
}
// La suite ...
// Implémentation OBLIGATOIRE des méthodes de// l'interface implémentée
public double calculeSurface() { return l*h;
}
public double calculePourtour() { return 2*(l+h);
}
}
Important: toutes les méthodes de l'interface doivent être implémentées
Exemple 2: la classe Cercle
public class Cercle extends Figure implements Mesurable {private double x,y;private double r;
public Cercle(String nom,double x,double y,double r) {super(nom);this.x=x;this.y=y;this.r=r;
}
public String getX() { return x; }
public String getY() { return y; }
public String getRayon() { return r; }
public void translater(double tx,double ty) {x+=tx;y+=ty;
}
// Ca continue ...
public class Cercle extends Figure implements Mesurable {private double x,y;private double r;
public Cercle(String nom,double x,double y,double r) {super(nom);this.x=x;this.y=y;this.r=r;
}
public String getX() { return x; }
public String getY() { return y; }
public String getRayon() { return r; }
public void translater(double tx,double ty) {x+=tx;y+=ty;
}
// Ca continue ...
Exemple 2: la classe Cercle (suite)// La suite ...
// Implémentation OBLIGATOIRE des méthodes de// l'interface implémentée
public double calculeSurface() { return Math.PI*r*r;
}
public double calculePourtour() { return 2*Math.PI*r;
}
}
// La suite ...
// Implémentation OBLIGATOIRE des méthodes de// l'interface implémentée
public double calculeSurface() { return Math.PI*r*r;
}
public double calculePourtour() { return 2*Math.PI*r;
}
}
Important: toutes les méthodes de l'interface doivent être implémentées
Représentation UML (point de vue client)
Mesurable+ calculeSurface() : double+ calculePourtour() : double
«interface»Figure
+ getNom() : String+ translater(tx: double, ty : double) : void+ toString() : String
« abstract »
Rectangle
+ Rectangle(x : double, y : double, l : double, h : double)+ getX() : double + getY() : double+ getLongueur() : double + getHauteur() : double+ translater(tx : double, ty : double) : void+ calculeSurface() : double+ calculePourtour() : double
Cercle
+ Rectangle(x : double, y : double, l : double, h : double)+ getX() : double + getY() : double+ getLongueur() : double + getHauteur() : double+ translater(tx : double, ty : double) : void+ calculeSurface() : double+ calculePourtour() : double
Lien d'implémentation
Relation implementsclasse C implements interface I
C « EST » I (ou « EST UN » mais plus rare)ex. Rectangle « EST » Mesurableex. Cercle « EST » Mesurableex. Figure « EST » Mesurable => ne marche pas !
Représentation UML:I
+ mehode(T1,...,TN) : T
«interface»
C+ mehode(T1,...,TN) : Tetc...
Les interfaces en pratique
1) Capacités communes (ex. Mesurable)● favorise le polymorphisme
2) Traverser des hiérarchies de classes séparées● compatibilité entre classes sans super-classe commune
3) Suppléer l'héritage● «économiser» le lien d'héritage● simuler l'héritage multiple
4) Cas particuliers● interfaces marqueurs (ex. Clonable, Serializable)● interfaces standards (ex. Comparable)
(1) Capacités communes
Des classes distinctes partagent une même capacité.
ex. Rectangle et Cercle partagent la capacité d'être Mesurable
... mais cette capacité dépasse le cadre de leur(s) super-classe(s)
ex. Rectangle et Cercle héritent de Figure mais toutes les Figure ne sont pas forcément Mesurable
favorise le polymorphismesi on utilise le type de l'interface, ex. Mesurable
on peut référencer des instances de n'importe quelle classe implémentant l'interface
Exemple: surface totale
public static double calculeSurface(ArrayList<Mesurable> figs) {double surface = 0.0;for(Mesurable fig : figs) {
surface += fig.calculeSurface(); // OK car mesurable ...}return surface;
}
// dans le main ...public static void main(String[] args) {ArrayList<Mesurable> list = new ArrayList<Mesurable>();list.add(new Cercle(2,2,3)); // OK car mesurable ...list.add(new Rectangle(2,2,3,4)); // OK car mesurable ...// list.add(new Point(2,2)); // KO car non-mesurable ...System.out.println("Surface totale = "+calculeSurface(list));
}
public static double calculeSurface(ArrayList<Mesurable> figs) {double surface = 0.0;for(Mesurable fig : figs) {
surface += fig.calculeSurface(); // OK car mesurable ...}return surface;
}
// dans le main ...public static void main(String[] args) {ArrayList<Mesurable> list = new ArrayList<Mesurable>();list.add(new Cercle(2,2,3)); // OK car mesurable ...list.add(new Rectangle(2,2,3,4)); // OK car mesurable ...// list.add(new Point(2,2)); // KO car non-mesurable ...System.out.println("Surface totale = "+calculeSurface(list));
}
En fait: Cercle et Rectangle sont (aussi) des sous-types de Mesurable
2) Hiérarchies séparées
Dans l'exemple précédent, Rectangle et Cercle ont une même super-classe (Figure)
... mais on peut bien sûr dépasser ce cadreSoit une classe B héritant de A (ex.: B=Rectangle, A=Figure)Soit une classe D héritant de C (ex.: D=Chambre, C=Piece)
Soit I une interfaceB implémente I (ex.: Rectangle implémente Mesurable)D implémente I (ex.: Chambre implémente Mesurable)
Compatibilités:B est compatible avec B, A et ID est compatible avec D, C et I
... donc les instance de B et D sont toutes deux utilisables avec le même type I (=> polymorphisme)
but: « Traverser » les hiérarchies séparées
Figure
+ translater(tx : double, ty : double) : void+ toString() : String
« abstract»
Polygone
+ getSurface() : double+ mettreEchelle(sx : double, sy : double)
« abstract»
Point
+ calculerDistance(Point P2) : double
Segment
+ getLongueur() : double
RectangleTriangle
Decoration
Dessert
- nom : String
# Dessert(String nom)+ getNom() : String+ decorer(Nappage nappage) : void+ « abstract » fabriquer() : void+ toString() : String
« abstract»
Patisserie
+ Patisserie(String nom)+ decorer(Decoration deco) : void+ fabriquer() : void
FruitNappage
« abstract
»
Gateau
+ Gateau(String nom)+ decorer(Fruit fruit) : void+ fabriquer() : void
Mousse
+ Mousse(String nom)+ decorer(Fruit fruit) : void+ fabriquer() : void
Mêmes capacités Interface
3) Suppléer l'héritage
« Coût » du lien d'héritage élevéon ne peut hériter que d'une super-classeRemarque: certains langage autorisent l'héritage multiple (ex. C++, Python)
pourquoi pas Java ? Problèmes sémantiqueconflits de signaturesdispatch dynamique implicite trop coûteuxhéritage « en diamant »
Mais de temps en temps (assez rarement en pratique)=> on veut modéliser:
C « EST UN » A et C « EST UN » B (aussi)ex.: Hydravion « EST UN » Bateau... et Hydravion « EST UN » Avion
Solution possible: interfaces
Classe abstraite VehiculeInterface IAvionInterface IBateauClasse concrète Hydravion
hérite de Vehicule (Hydravion « EST UN » Vehicule)implémente IAvion (Hydravion « EST UN » IAvion)implémente IBateau (Hydravion « EST UN » IBateau)
Limitation des interfaces: pas d'implémentationimpossible de factoriser du code commun à tous les bateaux et tous les avions (solution: cf. Design patterns) : uniquement des signatures de méthodes
Exemple: interfaces
public interface IAvion { public void voler();
}
public interface IBateau { public void naviguer();
}
public class Hydravion extends Vehicule implements IAvion, IBateau {
// Contrat minimal: doit implémenter// 1) les méthodes abstraites de Vehicule (s'il y en a)// 2) les méthodes déclarées dans l'interface IAvion// 3) les méthodes déclarées dans l'interface Ibateau
public void voler() { System.out.println("Je vole"); }public void naviguer() { System.out.println("Je navigue"); }// etc.
}
public interface IAvion { public void voler();
}
public interface IBateau { public void naviguer();
}
public class Hydravion extends Vehicule implements IAvion, IBateau {
// Contrat minimal: doit implémenter// 1) les méthodes abstraites de Vehicule (s'il y en a)// 2) les méthodes déclarées dans l'interface IAvion// 3) les méthodes déclarées dans l'interface Ibateau
public void voler() { System.out.println("Je vole"); }public void naviguer() { System.out.println("Je navigue"); }// etc.
}
Remarque: pas de limite sur le nombre d'interfaces implémentées
4) Cas particuliers
Interfaces Marqueursaucune méthode à implémentermais évoque une propriété commune
ex. interface Clonable => classe avec clone publiccf. cours 4
ex. interface Serializable => repr. externeetc.
Interfaces Standardsex. interface Comparablepour introduire un ordre entre instances d'une classeméthode public int compareTo(Object o)
cf. annales
Conclusion
Interfaces: outils d'abstractionUniquement des signatures de méthodes publiques(= type)Utile pour: polymorphisme, capacités partagées, etc.
Paquetages: outils de modularisationRassemblement de classes partageant un «objectif» communnouveaux points de vue:
héritier/paquetage: pour les sous-classes du même paquetage