• Aucun résultat trouvé

Héritage presque multiple (1/2)

N/A
N/A
Protected

Academic year: 2021

Partager "Héritage presque multiple (1/2)"

Copied!
25
0
0

Texte intégral

(1)

Utiliser deux classes ou plus dans la définition d'une nouvelle classe peut se faire par composition.

Héritage presque multiple (1/2)

class Etudiant{

int numero;

Diplome d;

...

float passeExamen(Examen e){

// retourne la note }

...

}

class Fetard{

...

void participeFete(Fete f){

...

} ...

}

class EtudiantFetard extends Etudiant{

Fetard f;

...

float participeFete(Fete f){

return this.f.participeFete(f);

} ...

}

(2)

Réutiliser deux classes ou plus dans une nouvelle classe peut aussi se faire par héritage multiple, mais cela pose des problèmes de polymorphisme en cas de résolution dynamique.

Une interface est une abstraction de classe et ne déclare que les signatures des fonctions.

L'appel de fonction d'une interface est donc impossible.

Héritage presque multiple (2/2)

class Truc extends Chose, extends Machin{

Truc(){

this.b; ??

this.m(); ??

}

class Machin{

Bidule b;

void m(){...}

} interface Chose{

void m();

}

class Chose{

Bidule b;

void m(){...}

}

(3)

Interface (1/3)

Une interface est une liste de méthodes abstraites (signature uniquement) et de constantes (le mot-clé final n'est pas nécessaire car implicite).

Une interface ne peut être instanciée et n'a donc pas de constructeur.

Une interface peut hériter d'une ou plusieurs autres interfaces.

Le polymorphisme d'attribut peut être géré à la compilation.

interface Fetard{

final float ALCOOLEMIE_MAX = 5.0f;

void participeFete(Fete f);

void faitLePlein(float litres);

}

interface Conducteur{

final float ALCOOLEMIE_MAX = 0.5f;

void conduit(Voiture v);

void faitLePlein(float litres);

}

interface FetardConducteur extends Fetard, Conducteur{

}

(4)

Une classe peut « hériter » d'autant d'interfaces qu'elle veut en utilisant le mot-clé implements.

Une classe qui implémente une interface doit rédéfinir les méthodes abstraites, sinon la classe est abstraite.

Interface (2/3)

class EtudiantFetardConducteur extends Etudiant implements Fetard, Conducteur{

...

float participeFete(Fete f){

...

}

void conduit(Voiture v){

...

}

void faitLePlein(float litres){

...

}

...

this.ALCOOLEMIE_MAX ?? le compilateur oblige à préciser ...

}

(5)

Une interface définit un type de données abstrait, mais qui peut être utiliser pour typer des variables.

On peut donc définir un ensemble, hiérarchisé par héritage, de types abstraits (non instanciables) en plus des types concrets (instanciables).

Ces types abstraits ne servent pas qu'à faire de l'héritage presque multiple, mais aussi à structurer davantage les programmes.

Interface (3/3)

Fetard f = new EtudiantFetardConducteur();

f.faitLePlein(2.0f);

f.conduit(new DeuxChevaux());

f.ALCOOLEMIE_MAX = 3.0f;

...

Conducteur[] liste = new Conducteur[3];

liste[0] = new Conducteur();

...

(6)

Classe abstraite (1/4)

Une classe qui implémente une (ou plusieurs) interface(s) sans redéfinir toutes les méthodes abstraites est également abstraite et doit être déclarée telle avec le mot-clé abstract.

Une classe abstraite ne peut être instanciée.

Une classe dont la définition est préfixée par abstract est abstraite, même si elle n'implémente pas d'interface et n'a pas de méthode abstraite.

abstract class CapitaineDeSoiree extends Etudiant implements Fetard, Conducteur{

float participeFete(Fete f){

...

}

void faitLePlein(float litres){

...

} }

(7)

Classe abstraite (2/4)

Une classe abstraite peut être utile lorsqu'on veut n'implémenter qu'une partie des méthodes d'une interface.

abstract class CapitaineDeSoiree extends Etudiant implements Fetard, Conducteur{

float participeFete(Fete f){

...

}

void faitLePlein(float litres){

...

} }

class ChauffeurFiable extends CapitaineDeSoiree{

void conduit(Voiture v){

v.bienConduire();

} }

class ChauffeurNonFiable extends CapitaineDeSoiree{

void conduit(Voiture v){

v.conduireNimporteComment();

}

(8)

Classe abstraite (3/4)

Une classe définit une méthode abstraite en donnant sa signature et en la préfixant par le mot-clé abstract.

Toute classe qui définit une méthode abstraite est abstraite et doit être déclarée comme telle.

abstract class CapitaineDeSoiree extends Etudiant implements Fetard, Conducteur{

...

abstract void drague(Personne p);

}

class CapitaineDeSoireeEnCouple extends CapitaineDeSoiree{

...

void drague(Personne p){}

}

class CapitaineDeSoireeCelibataire extends CapitaineDeSoiree{

...

void drague(Personne p){

this.payeUnVerre(p);

this.danseAvec(p);

...

(9)

Classe abstraite (4/4)

Une classe abstraite peut être utile pour imposer l'existence de certaines méthodes dans des classes, sans savoir comment elles seront implémentées.

Une classe qui hérite d'une classe abstraite doit redéfinir les méthodes abstraites, sinon elle est abstraite également.

En C++, les méthodes abstraites sont dites virtuelles pures :

abstract class Animal{

...

abstract String cri();

}

class Chat extends Animal{

...

String cri(){

return "miaou";

} } class Chien extends Animal{

...

String cri(){

return "ouahouah";

} }

virtual void methodeAbstraite() = 0;

(10)

Interface ou classe abstraite?

Une interface sert à définir un comportement :

- quand on veut définir uniquement la spécification du comportement d'une catégorie d'objets.

- quand on veut faire de l'héritage presque multiple.

Une classe abstraite, comme toute classe, sert à factoriser du code :

- quand on veut définir le comportement d'une catégorie d'objets via des méthodes abstraites et des méthodes concrètes.

Un programme n'est jamais trop abstrait, il ne faut pas hésiter à créer des interfaces et des classes abstraites qui structurent le code et facilitent sa maintenance, son extensibilité et sa réutilisation.

(11)

Abstraction et qualité du logiciel (1/2)

class Animal{

...

String cri(){

if(this instanceof Chat) return "miaou";

if(this instanceof Chien) return "ouahouah";

if(this instanceof Canari) return "cuicui";

} }

class Chat extends Animal{

...

String cri(){

return "miaou";

class Canari extends Animal{

...

String cri(){

return "cuicui";

abstract class Animal{

...

abstract String cri();

}

class Chien extends Animal{

...

String cri(){

return "ouahouah";

} }

Bidouille :

Programme de qualité :

(12)

Patron de conception

Interfaces et classes abstraites sont combinées dans des patrons de conception (design pattern) pour répondre aux problèmes pratiques de programmation objet.

Exemple : le pattern Strategy, permettant de changer dynamiquement le comportement des instances d'une classe au cours de l'exécution d'un programme.

(13)

Membre de classe

Une classe est une description, à un niveau plus abstrait, des objets qui en sont les instances.

Mais il peut être utile de décrire des données ou des comportements véritablement communs à toutes les instances :

- attributs avec la même valeur pour toutes les instances

- méthodes s'exécutant de la même façon pour toutes les instances

class Quadrupède{

int nbPattes = 4;

...

void classerParPoids(Quadrupède[] t){

...

} }

(14)

Attribut de classe (1/2)

Un attribut de classe, préfixé par le mot-clé static, est lié à sa classe et non aux instances. Sa valeur est donc la même pour tous les objets.

Un attribut de classe peut être accédé via la classe ou via une instance.

Une méthode d'instance peut accéder/modifier un attribut de classe (la valeur change alors évidemment pour toutes les instances).

class Humain{

String nom;

static Humain ancetre = new Humain("Lucy");

...

public static void main(String[] pouet){

Humain h = new Humain("Toto");

Humain l = h.ancetre;

l = Humain.ancetre;

System.out.println(ancetre);

} }

(15)

Attribut de classe (2/2)

L'initialisation des attributs de classe peut se faire à la déclaration des attributs, ou dans un bloc static exécuté au chargement de la classe.

Un attribut de classe non modifiable (final) est une constante de classe.

class Humain{

String nom;

static Humain ancetre = new Humain("Lucy");

static ArrayList<Droit> ddl;

static{

ddl = new ArrayList<Droits>();

ddl.add(new Droit("Liberté"));

ddl.add(new Droit("Egalité en droit"));

...

} ...

}

Math.PI;

(16)

Méthode de classe (1/2)

Une méthode de classe, préfixée par static, s'exécute de la même façon, à partir d'une classe ou d'une instance.

class Humain{

...

static void ajoutDroit(Droit d){

ddl.add(d);

}

static void premierPasSur(Planete p){

System.out.println("un grand pas pour l'humanité");

} ...

public static void main(String[] pouet){

Humain h = new Humain("Toto");

h.premierPasSur(lune);

Humain.premierPasSur(mars);

Humain.ajoutDroit(new Droit("Mourir dans la dignité");

} }

(17)

Méthode de classe (2/2)

Une méthode de classe ne possède pas d'objet courant (pas de this) donc elle ne peut pas accéder aux membres d'instance

.

La méthode main est obligatoirement de classe car elle sert de point d'entrée des programmes.

class Humain{

String nom;

...

void nait(){

System.out.println("Ouuiiiinnnnn");

}

static void premierPasSur(Planete p){

System.out.println("un grand pas pour l'humanité");

System.out.println("je m'appelle "+nom);

nait();

} ...

}

(18)

Membre de classe et héritage (1/2)

Les attributs de classe peuvent être redéfinis (masqués), les méthodes de classe peuvent être redéfinies ou surchargées.

Les appels aux attributs et méthodes de classe sont résolus à la compilation (résolution statique). Donc les membres de classe appelés sur un objet sont ceux de la classe qui type la variable qui stocke l'objet.

class Homme{

static Homme ancetre = new Homme("Adam");

...

static void premierPasSur(Planete p){

System.out.println("un grand pas pour l'homme");

}

public static void main(String[] s){

Humain h = new Homme("Toto");

System.out.println(h.ancetre.nom);

h.premierPasSur(jupiter);

} ...

}

(19)

Membre de classe et héritage (2/2)

Il n'est pas possible de redéfinir une méthode de classe par une méthode d'instance ou inversement.

class A{

static void m1(){

...

}

void m2(){

...

} ...

}

class B extends A{

void m1(){

...

}

static void m2(){

...

} ...

}

(20)

Membre de classe et abstraction

Une méthode de classe ne peut être abstraite car sa classe doit définir le corps de la méthode.

class Humain{

...

static abstract void premierPasSur(Planete p);

public static void main(String[] pouet){

Humain.premierPasSur(lune); ??

} }

interface Enfant{

final static Personnage pere_noel = ...;

Personnage ...

void jouer(Jeu j);

void faire_des_betises();

...

}

Les attributs constants des interfaces sont implicitement des attributs de classe (mais le mot- clé static n'est pas nécessaire).

(21)

Métaprogrammation en Java (1/4)

En Java, les classes sont chargées dans la machine virtuelle lorsque c'est nécessaire uniquement, en cas de création d'instance ou lors d'appel de méthode de classe.

Les classes du noyau de l'API Java sont chargées par défaut.

Ce chargement paresseux (lazy loading) permet de modifier dynamiquement les programmes. Ce n'est pas le cas en C++.

La JVM, ou les programmes eux-mêmes, peuvent charger des classes au cours de l'exécution des programme par des instances de Classloader (classe abstraite).

Un ClassLoader permet de récupérer un objet de type Class qui représente une classe. Dans la JVM, il peut exister au même moment plusieurs ClassLoader qui ont chargé les mêmes classes.

(22)

Métaprogrammation en Java (2/4)

Il existe plusieurs méthodes pour récupérer les instances de Class.

Les tableaux et les types primitifs (boolean, int, etc) sont aussi des types représentés par des instances de Class via les classes correspondantes (Boolean, Integer, etc).

Il est impossible de créer dynamiquement de nouvelles classes en appelant un constructeur de Class. Seul le chargement de classes préalablement définies est possible.

ClassLoader cl = Homme.class.getClassLoader();

Class c = cl.loadClass("Humain");

Class d = Class.forName("Femme");

Humain h = new Humain("Toto");

h.getClass();

(23)

Métaprogrammation en Java (3/4)

La classe Class permet d'accéder à des instances représentant les membres des classes.

La classe Class permet de créer des instances si la classe est instantiable et possède un constructeur sans paramètre.

ClassLoader cl = Homme.class.getClassLoader();

Class c = cl.loadClass("Humain");

Field[] attributs = c.getFields(); // attributs hérités ou non Method[] methodes = c.getMethods(); // méthodes héritées ou non Constructor[] constructs = c.getConstructors();

Class sc = c.getSuperclass();

Class[] inter = c.getInterfaces();

...

Class c = cl.loadClass("Humain");

Humain h = (Humain) c.newInstance();

...

(24)

Métaprogrammation en Java (4/4)

Les classes Field et Method permettent de manipuler les attributs et méthodes des classes.

Field f = ... // f est un champ de la classe C String s1 = f.getName();

Class cl1 = f.getType();

Class dc1 = f.getDeclaringClass();

boolean stat1 = Modifier.isStatic(f.getModifiers());

boolean fin1 = Modifier.isFinal(f.getModifiers());

Object o = f.get(obj); // o contient la valeur de f sur l'objet obj ...

Object o = ... // o est une instance de C Object v = ... // v est une instance de cl1 f.set(o,v);

Method m = ... // m est une méthode de la classe C String s1 = m.getName();

Class cl1 = m.getReturnType();

Class[] param1 = m.getParameterTypes();

Class dc1 = m.getDeclaringClass();

boolean stat1 = Modifier.isStatic(m.getModifiers());

boolean fin1 = Modifier.isFinal(m.getModifiers());

...

Object o = ... // o est une instance de C

(25)

Introspection

La modularité et l'abstraction permet l'introspection des programmes objets : un programme peut s'auto-analyser ou analyser des objets chargés au cours de l'exécution, même si leurs types ne sont pas connus lors de l'écriture du programme.

→ l'introspection améliore donc la généralité des programmes.

→ les applications réparties peuvent interagir par introspection avec n'importe quel programme objet sans avoir à connaître à l'avance la structure de ce programme.

→ les IDE (Integrated Development Environment) utilisent également l'introspection pour l'aide à l'écriture et au test des programmes.

→ les tests logiciels peuvent être en partie automatisés

L'introspection peut poser des problèmes de robustesse et de sécurité et doit être contrôlée par les mécanismes d'encapsulation.

Références

Documents relatifs

Une classe dérivée d’une classe non abstraite peut être déclarée abstraite et/ou contenir des méthodes abstraites... Java Classes et

Dilemme, dont la seule issue possible est d’installer une méthode evolue() dans la classe Ressource , tout en déclarant cette méthode « abstraite », c’est-à-dire sans

On constate qu’une classe abstraite (exemple BufferedReader) contient au moins une méthode abstraite (exemple : abstract void close()). Création d’un fichier

Toutefois, une classe abstraite peut implémenter une interface avec des méthodes abstraites qui seront donc finalement implémentées par les méthodes des classes réalisant

1) a) Commencer par télécharger et installer Algobox. Algobox est gratuit, tapez « Algobox » dans n'importe quel moteur de recherche pour le trouver ou allez

Libre à vous de le compléter comme bon vous semble en utilisant au mieux les différentes notions vues en cours et en TD (en vrac : méthodes et classes abstraites, méthodes et

Nous souhaitons pouvoir transformer les figures: translater, mettre à l’échelle, calculer des distances et des surfaces (figures fermées), et bien sûr afficher les figures... Etude

Avec cinq graduations intermédiaires au minimum, celui-ci donne 40 configurations possibles qui incluent les deux qui viennent d’être mentionnées.. Pour L variant de 24