Héritage et abstraction et interfaces

Relation d’héritage

L’héritage une relation de spécialisation/généralisation.

Héritage en Java

1
2
3
class Livre extends Article {
    ...
}

Le mot clé extends en Java n’a rien à voir avec le mot clé extends en UML, employé en particulier dans les diagrammes de cas d’utilisation.

Sans héritage

Héritage des parents

La classe enfant (celle qui hérite) possède toutes les propriétés de ses classes parents (attributs et opérations)

Elle n’a accès qu’aux propriétés publiques (comme tout le monde) et protégées de ses parents. Le propriétés publiques restent masquées pour les enfants.

Terminologie

La signature d’une opération est constituée de son nom et de ses paramètres (pas de sa valeur de retour).

La surcharge d’opérations (même nom, mais signatures des opérations différentes) est possible dans toutes les classes.

Polymorphisme :

Substitution : une instance d’une classe peut être utilisée partout où une instance de sa classe parent est attendue (par exemple, toute opération acceptant un objet d’une classe Animal doit accepter tout objet de la classe Chat (l’inverse n’est pas toujours vrai).

Classe abstraite

Une opération est dite abstraite lorsqu’on connaît sa signature mais pas la manière dont elle peut être réalisée (en UML, on écrit son nom en italique).

Une classe est dite abstraite lorsqu’elle définit au moins une méthode abstraite ou lorsqu’une classe parent contient une méthode abstraite non encore réalisée (en UML, on écrit son nom en italique).

Il est impossible d’instancier directement une classe abstraite.

Héritage multiple

Une classe peut avoir plusieurs classes parents. On parle alors d’héritage multiple.

L’emploi de l’héritage multiple est souvent peu justifié et d’autres solutions de conception sont souvent préférables.

Interface : utilisation

Les interfaces sont très utilisées en COO et en POO. On les utilise d’ailleurs plus régulièrement que l’héritage, par exemple.

Le rôle d’une interface est de regrouper un ensemble d’opérations assurant un service cohérent que des classes sont susceptibles d’offrir. On peut ainsi définir des classes clientes (ici, Page) avant d’avoir défini toutes les implémentations requises de l’interface.

Selon le principe de substitution, on pourra employer une classe réalisant une interface partout où l’interface est attendue.

Interface : définition

Notation lollipop des interfaces

Compléments sur les diagrammes de classes

Associations réflexives

L’association la plus utilisée est l’association binaire (reliant deux classes).

Parfois, les deux extrémités de l’association pointent vers la même classe. Dans ce cas, l’association est dite réflexive.

Instantiation d’associations réflexives

Instanciée, une association réflexive fait intervenir plusieurs objets. Par exemple :

Associations n-aires

Artité des associations :

Il arrive que ce ne soit pas suffisant. Dans ce cas, on peut définir des relations n-aires.

Attributs de classe

Opérations de classe

Classe paramétrée

Pour définir une classe générique et paramétrable en fonction de valeurs et/ou de types :

Paquetages

Il est recommandé de mettre dans des paquetages (packages) différents les éléménts qui ont un raport plus étroi les uns avec les autres.

Ici, par exemple, on identifie un package pour les éléments liés aux aspects commerciaux, et un autre pour les éléménts concernant le catalogue de produits.

A la manière de dossiers, on peut imbriquer des packages dans d’autres packages.

Chaque classe dispose d’un nom absolu et non ambigu Package::SousPackage::NomClasse. Exemple : java.lang.String.

Impact des associations sur un code en java

Association unidirectionnelle * vers 1

Ce type d’association est le plus courant, et heureusement le plus simple à impélmenter.

1
2
3
4
class A {
    private B b;
    (getter et setter publics)
}

Association unidirectionnelle * vers *

1
2
3
4
class A {
    List<B> b;
    (getter, adder et remover publics)
}

Association bididirectionnelle 1 vers 1

Il faut maintenir la cohérence des référencements dans les deux sens. Un simple

1
2
3
4
5
6
7
8
9
10
public class A {
    private B b;
    public void setB(B b) {
        if ((b!=null) && (b.getA()!=null))  // si b est déjà connecté à un autre A
            b.getA().setB(null);            // cet autre A doit se déconnecter
            this.b=b;
            b.setA(this);
    }
    public B getB() {return(b);}
}

Il faut également définir l’opération symétrique dans B.

Association unidirectionnelle vers 1

C’est paradoxalement plus complexe que le cas bidirectionnel :

En pratique, on peut réfléchir à la nécessite de la multiplicité maximale de 1 à la source.

Si elle est vraiment justifiée, on peut admettre qu’un développeur code en réalité une association bi-directionnelle et joue sur les modificateurs d’accès pour rendre la relation plus asymétrique.

Association bidirectionnelle 1 vers *

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class A {
    private List <B> b;
    public A() {this.b = new List<B>(); }
    public List<B> getList() {return(this.b);}
    public void remove(B b){this.b.remove(b);}
    public void addB(B b){
        if(!this.b.contains(b)){ 
            if (b.getA()!=null) b.getA().remove(b);
            b.setA(this);
            this.b.add(b);
        }
    }
}

public class B {
    private A a;
    public A getA() {return (this.a);}
    public void setA(A a){ 
        if((a!=null) && (!a.get().contains(this)) { 
            if (this.a != null) this.a.remove(this);
            this.setA(a);
            this.a.get().add(this);
        }
    }
}

Association bidirectionnelle * vers *

A faire en exercice pour la prochaine scéance ;)

Classe association unidirectionnelle * vers 1

1
2
3
4
5
6
7
8
9
10
11
12
class A {
    private B b;
    private C c;
    (getB, setB et éventuellement getC)
    public getX {return this.x.getX();}
    public setX(x:X) {this.x.setX(x);}
}

class C {
    private X x;
    (getter et setter publics)
}

Classe association unidirectionnelle * vers 1 (plus simple)

1
2
3
4
5
class A {
    private B b;
    private X x;
    (getter et setter publics)
}

Classe association unidirectionnelle * vers *

1
2
3
4
5
6
7
8
9
10
11
12
13
class A {
    private List<C> c;
    (getter et setter publics)
    public getB {return this.c.getB();}
    public setB(b:B) {this.c.setB(b);}
    public getX {return this.c.getX();}
    public setX(x:X) {this.c.setX(x);}
}
class C {
    private B b;
    private X x;
    (getters et setters publics)
}

Classe association bidirectionnelle * vers *

En pratique, on se ramène à un cas comme :

A faire également pour la prochaine scéance ;)


Module d’UML