OVH Cloud OVH Cloud

Heritage multiple vs. composition

22 réponses
Avatar
Olivier Croquette
Bonjour,

je voudrais votre avis sur une utilisation de l'héritage multiple qui ne
parait pas courante.

Soit une classe véhicule, dont l'utilisateur doit pouvoir déterminer la
position et à la couleur.

Soit une implémentation basée sur l'héritage multiple:

class Color {
public:
SetColor(...);
protected:
double r,g,b,a;
}

class Position {
public:
SetPosition(...);
protected:
double x,y;
}


class Vehicule : public Position, public Color {
...
}

Note: les classes Vehicule, Position, Color sont spécifiées et
implémentées par les mêmes développeurs (ie. pas d'héritage multiple de
classes "externes").


En gros, l'héritage multiple est utilisé pour ajouter en même temps une
interface et une implémentation à une classe.


Les avantages de cette implémentation sont les suivants:

- syntaxe très simple au niveau de Vehicule pour rajouter de nouvelles
propriétés

- très peu d'effort au niveau de Vehicule pour rajouter de nouvelles
propriétés

- Vehicule hérite automatiquement de toutes amélioration à Position
Par exemple, si SetPositionX() et SetPositionY() sont implémentées
dans Position(), Vehicule en profite automatiquement.

- syntaxe très simple pour l'utilisateur, qui n'a qu'à qu'à faire
monvehicule.SetColor()

Une limitation (au moins) est qu'on est embêté si le véhicule doit un
jour devenir bicolore.

A part ça, l'héritage multiple étant souvent sujet à controverse, y
a-t'il un risque particulier à une telle implémentation?
Quels sont les précautions à prendre?

10 réponses

1 2 3
Avatar
Fabien LE LEZ
On Sat, 12 Aug 2006 13:26:22 +0200, Olivier Croquette
:

class Position {
public:
SetPosition(...);
protected:
double x,y;
}

class Vehicule : public Position, public Color {


Je vois au moins trois problèmes :

1- Une position n'a pas de position.

Si tu écris
Position ma_position;
ma_position.SetPosition (...);
tu écris "la position de la position ma_position est ...".
Ça n'a pas de sens.

2- Ton héritage indique qu'un véhicule *est* une position.
Là non plus, ça n'a pas de sens.

En fait, pour régler ces deux problèmes, il suffit de changer le nom
de la classe, et de l'appeler "ObjetAyantUnePosition".
On peut alors avoir une fonction "SetPosition" (un
"objet ayant une position" a bien une position) ; et un "Véhicule" est
bien un cas particulier d'objet ayant une position.

3- Des variables membres presque publiques, puisqu'une autre classe
peut y accéder. Tôt ou tard, ça te causera des problèmes.
D'une manière générale, toutes les variables membres doivent être
privées.

Avatar
Fabien LE LEZ
On Sat, 12 Aug 2006 13:26:22 +0200, Olivier Croquette :

class Position {
public:
SetPosition(...);
protected:
double x,y;
}


J'oubliais les deux autres problèmes, qui sont sans doute des fautes
de frappe :
- il manque le point-virgule à la fin ;
- il manque le destructeur virtuel.

Avatar
Sylvain
Fabien LE LEZ wrote on 12/08/2006 13:37:

1- Une position n'a pas de position.


au primaire on apprend "être" et "avoir"; ici une position est un état.

Si tu écris
Position ma_position;
ma_position.SetPosition (...);
tu écris "la position de la position ma_position est ...".
Ça n'a pas de sens.


alors que dans "Position point;" 'point' a bien une position; voulais-tu
seulement dire qu'il peut toujours exister une (mauvaise) façon de
présenter qui permet de balayer le fond en raillant la forme ?

2- Ton héritage indique qu'un véhicule *est* une position.
Là non plus, ça n'a pas de sens.


tu disais "a une position", tu te contredis juste pour contredire le PO?
une véhicule a bien une position, non ?

En fait, pour régler ces deux problèmes, il suffit de changer le nom
de la classe, et de l'appeler "ObjetAyantUnePosition".


ou encore
"ClasseQuiPeutEtreEtenduParQuelqueChoseQuiPeutEtreOuAvoirUnePosition",
mais bon c'est un peu long ...

3- Des variables membres presque publiques, puisqu'une autre classe
peut y accéder. Tôt ou tard, ça te causera des problèmes.
D'une manière générale, toutes les variables membres doivent être
privées.


d'une manière générale, il faut se méfier des généralités ;)

Sylvain.

Avatar
Alain Gaillard


A part ça, l'héritage multiple étant souvent sujet à controverse, y
a-t'il un risque particulier à une telle implémentation?
Quels sont les précautions à prendre?


AMHA il n'est pas ici question de controverse à propos d'héritage multiple.
Normalement (comme l'a souligné Fabien), l'héritage exprime une relation
"est-un". Comme par exemple une voiture de sport pourrais dériver d'une
simple voiture.
Une voiture n'est ni une position ni une couleur, mais a une position et
a une couleur. La composition me semble beaucoup plus appropriée dans ce
cas. Et du coup tu élimines les problèmes potentiels de l'héritage
multiple :-)


--
Alain

Avatar
Alain Gaillard


A part ça, l'héritage multiple étant souvent sujet à controverse, y
a-t'il un risque particulier à une telle implémentation?
Quels sont les précautions à prendre?


AMHA il n'est pas ici question de controverse à propos d'héritage multiple.
Normalement (comme l'a souligné Fabien), l'héritage exprime une relation
"est-un". Comme par exemple une voiture de sport pourrait dériver d'une
simple voiture.
Une voiture n'est ni une position ni une couleur, mais a une position et
a une couleur. La composition me semble beaucoup plus appropriée dans ce
cas. Et du coup tu élimines les problèmes potentiels de l'héritage
multiple :-)


--
Alain

Avatar
Arnaud Debaene
"Olivier Croquette" a écrit dans le message
de news: ebkdsv$2k5$00$
Bonjour,

je voudrais votre avis sur une utilisation de l'héritage multiple qui ne
parait pas courante.

Soit une classe véhicule, dont l'utilisateur doit pouvoir déterminer la
position et à la couleur.

Soit une implémentation basée sur l'héritage multiple:

class Color {
public:
SetColor(...);
protected:
double r,g,b,a;
}

class Position {
public:
SetPosition(...);
protected:
double x,y;
}


class Vehicule : public Position, public Color {
...
}


Lors d'une conception objet, il est toujours bon de se rappeler du principe
de substitution de Liskov (LSP) : Il ne faut utiliser l'héritage publique
que lorsque un objet dérivé "est" également un objet de la classe de base,
et peut prendre sa place dans tous les usages que l'on peut faire de la
classe de base.

Dans ton cas, tu est en train de dire :
- qu'une voiture EST une couleur. Donc notamment et au hasard, qu'on peut
"additionner" ou "soustraire" 2 voitures pour faire des mélanges
chromatiques.
- qu'une voiture EST une position. D'une part, cela sous-entend que Position
est une classe mutable (qui peut changer dans le temps - sinon la voiture
est immobilisée), mais pourquoi pas... Ensuite, une paire de voiture forment
un vecteur spatial....

Mes exemples sont tirés par les cheveux, mais c'est pour te faire sentir ce
que l'on entend généralement par héritage publique, et pourquoi ce n'est pas
adapté dans to cas. La sur-utilisation de l'héritage est une erreur
courante, et entraine toujours des problèmes de maintenabilité et
d'évolutivité à long terme.

Au passage, dans les langages qui n'implémentent pas l'héritage multiple
(C#, Java, etc...), un argument souvent avancé pour justifier leur choix est
d'empêcher les programmeurs de faire ce genre de mauvais usage de l'héritage
multiple et les "forcer" à faire le bon choix, celui de la composition.

Arnaud

Avatar
Olivier Croquette
Merci pour vos commentaires!

Je me doutais bien que beaucoup déconseilleraient ce design, mais
jusqu'à maintenant, je n'ai pas encore vu de description des problèmes
*précis* auxquels cela expose.

De mon côté, j'y vois le gros avantage de simplifier énormément les
écritures et de limiter la quantité de code trivial, genre:

void Vehicule::SetPosition(x,y) { this->position.Set(x,y); }

ou bien:

Position &Vehicule::GetPositionRef() { return this->position; }


et ce pour chaque attribut.
Avatar
Cyrille

Merci pour vos commentaires!

Je me doutais bien que beaucoup déconseilleraient ce design, mais
jusqu'à maintenant, je n'ai pas encore vu de description des problèmes
*précis* auxquels cela expose.


Il n'y a pas trop de problèmes, seul le nom des classes de bases est
foireux. Comme l'explique Fabien Le Lez, "ObjetAyantUnePosition" est
préférable à "Position".
Pourquoi? Parce que ça évite des confusions pour les utilisateurs de la
classe.
Ainsi, si je lis "Position", je pense à une classe concrète, copiable et
assignable, et bêtement j'aimerais pouvoir faire:

Position pos1;
Position &pos2 = .....
pos2 = pos1;

Or pos2 peut-être une référence vers un Vehicule, et là c'est le bordel...

De mon côté, j'y vois le gros avantage de simplifier énormément les
écritures et de limiter la quantité de code trivial, genre:

void Vehicule::SetPosition(x,y) { this->position.Set(x,y); }

ou bien:

Position &Vehicule::GetPositionRef() { return this->position; }


Oui, pourquoi pas, on peut aussi éviter les duplications avec des macros:

class Vehicule
{
IMPLEMENTE_POSITION()
IMPLEMENTE_COULEUR()
};

et ce pour chaque attribut.


Rien ne vous oblige à écrire des "this->", déjà :)

--
C'est ma signature qu'elle est la mieux. Pas la vôtre.

Avatar
Olivier Croquette
Cyrille wrote:
Rien ne vous oblige à écrire des "this->", déjà :)


OK, merci pour tous les avis!

En ce qui concerne les "this->" c'est une vieille habitude :)

Avatar
Alain Gaillard


Je me doutais bien que beaucoup déconseilleraient ce design, mais
jusqu'à maintenant, je n'ai pas encore vu de description des problèmes
*précis* auxquels cela expose.


Ca viendra :)


De mon côté, j'y vois le gros avantage de simplifier énormément les
écritures et de limiter la quantité de code trivial, genre:

void Vehicule::SetPosition(x,y) { this->position.Set(x,y); }




Mais alors si tu veux simplifier, peut être que véhicule ne doit pas
dériver de Position ni être composé, mais simplement avoir des membres
xpos et ypos et alors c'est tout simplement

void Vehicule::SetPosition(x,y) { xpos=x; ypos=y; }



--
Alain

1 2 3