Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Design d'OO

4 réponses
Avatar
DELVA Michael
Bonsoir à tous,

je me permets de faire un post différent pour une question que j'ai posé
la dernière fois.

Quelle solution vous utilisez concernant l'utilisation de méthodes
virtuelles dans une classe dérivée, qui appellent la même fonction de la
base:

//Méthode 1
class Base
{
public:
virtual void foo()
{
//some work
}
};

class Derived
{
public:
virtual void foo()
{
Base::foo();
// some other work
}
};

//Méthode 2
class Base
{
private:
virtual void fooImpl() = 0;
public:
void foo()
{
//some work
fooImpl();
}
};

class Derived
{
private:
virtual void fooImpl()
{
// some other work
}
};

L'avantage de la méthode 1 étant la simplicité, mais l'inconvénient étant
un oubli possible d'appeler Base::foo().

La seconde ayant l'avantage de ne pas pemettre l'omission de la
redéclaration de la fonction, mais est plus lourde à utiliser.

Merci pour vos conseils éclairés

Michael

4 réponses

Avatar
Michael DOUBEZ
Bonsoir à tous,

je me permets de faire un post différent pour une question que j'ai posé
la dernière fois.

Quelle solution vous utilisez concernant l'utilisation de méthodes
virtuelles dans une classe dérivée, qui appellent la même fonction de la
base:

//Méthode 1
class Base
{
public:
virtual void foo()
{
//some work
}
};

class Derived
{
public:
virtual void foo()
{
Base::foo();
// some other work
}
};

//Méthode 2
class Base
{
private:
virtual void fooImpl() = 0;
public:
void foo()
{
//some work
fooImpl();
}
};

class Derived
{
private:
virtual void fooImpl()
{
// some other work
}
};



Methode 1 et Methode 2 ne sont pas équivalente: dans la Methode 2
fooImpl est une virtuelle pure et donc Base ne peut pas etre instanciée.
La délégation à la base dérivée fait donc partie du contrat.

L'avantage de la méthode 1 étant la simplicité, mais l'inconvénient étant
un oubli possible d'appeler Base::foo().

La seconde ayant l'avantage de ne pas pemettre l'omission de la
redéclaration de la fonction, mais est plus lourde à utiliser.


Si Derived hérite publiquement de Base, j'utilise la Méthode 2. Sinon,
ça n'a pas grande importance mais j'irai quand même aussi vers la Méthode 2.

J'ai l'impression que avec Méthode 1, il y a un plus grand risque de
violer LSP. Je ne vois pas d'exemple où j'utiliserai cette méthode (à
par pour intégrer un code dont je n'ai pas le contrôle).


Michael

Avatar
James Kanze
On Oct 2, 10:59 pm, DELVA Michael wrote:
je me permets de faire un post différent pour une question que j'ai pos é
la dernière fois.

Quelle solution vous utilisez concernant l'utilisation de méthodes
virtuelles dans une classe dérivée, qui appellent la même fonction de la
base:


Je suis temté à dire que je ne le fais pas. En général, si une
fonction est virtuelle dans la base, elle est aussi virtuelle
pûre, sans implémentation, et ne peut donc pas être appelée.

//Méthode 1
class Base
{
public:
virtual void foo()
{
//some work
}
};

class Derived
{
public:
virtual void foo()
{
Base::foo();
// some other work
}
};


Dans la pratique, de tels cas me semblent bien rares.

//Méthode 2
class Base
{
private:
virtual void fooImpl() = 0;
public:
void foo()
{
//some work
fooImpl();
}
};

class Derived
{
private:
virtual void fooImpl()
{
// some other work
}
};


Ça, j'utilise souvent, mais pour d'autres raisons. Dans
Base::foo(), il n'y a pas "some work", mais simplement des
vérifications du contrat.

L'avantage de la méthode 1 étant la simplicité, mais
l'inconvénient étant un oubli possible d'appeler Base::foo().

La seconde ayant l'avantage de ne pas pemettre l'omission de
la redéclaration de la fonction, mais est plus lourde à
utiliser.


Le tout dépend du but. S'il est essentiel, du point de vue de
Base, que ça partie du travail ait lieu, alors, il n'y a que la
deuxième solution. Si en revanche, Base fournit une
implémentation partielle par commodité, que Derived peut
utiliser ou non, comme il veut, je ferais probablement quelque
chose du genre :

class Base
{
protected:
void partialFoo() { /*...*/ }

public:
void foo() = 0 ;
} ;

(Dans de tels cas, je préfère même deux classes : une classe de
base qui définit l'interface, et que l'interface, et une classe
d'implémentation partielle qui en dérive. L'auteur de la classe
dérivée finale peut alors choisir entre tout implémenter
lui-même, en dérivant de Base, ou à se servir de
l'implémentation partielle, en en dérivant.)

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Jean-Marc Bourguet
DELVA Michael writes:

L'avantage de la méthode 1 étant la simplicité, mais l'inconvénient étant
un oubli possible d'appeler Base::foo().

La seconde ayant l'avantage de ne pas pemettre l'omission de la
redéclaration de la fonction, mais est plus lourde à utiliser.


La premiere methode est aussi plus souple: dans la classe filel on peut
appeler le membre de la classe de base quand on veut.

Je crois que je me baserais sur ce critere pour choisir: si cette souplesse
est necessaire, la premiere methode, sinon, la deuxieme (avec
vraissemblablement un nom plus significatif que simplement suffixe par
Impl).

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Luc Hermitte
Salut,

On 3 oct, 09:38, Jean-Marc Bourguet wrote:
DELVA Michael writes:
L'avantage de la méthode 1 étant la simplicité, mais l'inconvén ient étant
un oubli possible d'appeler Base::foo().

La seconde ayant l'avantage de ne pas pemettre l'omission de la
redéclaration de la fonction, mais est plus lourde à utiliser.


La premiere methode est aussi plus souple: dans la classe fille on peut
appeler le membre de la classe de base quand on veut.


Pas si souple que ça. Car on est vite prisonniers de ce schéma qui
n'est valable que dans les cas où le traitement commun s'éffectue que
avant, ou que après.

Sur un code existant, j'avais un fonction open() faisant des choses
dans le cas général, et dans les cas spécialisés, d'autres choses
devaient être réalisées avant (obligatoirement) celles du cas gén éral.
Jusque là tout allait bien. Sauf que l'on avait oublié une mise à jour
d'état au début de l'ouverture.

Faire ça dans la fonction mère eu été bien, sauf que ces traitements
doivent être réalisés au départ, et pas après. Et faire ça dans
chacune des fonctions filles pas vraiment génial.

Résultat, il fallait casser la structure courante, et retirer des
fonctions dans les classes dérivées pour les remplacer par des
do_pre_open-like(). C'est pas que cela soit impossible, mais quand on
considère l'évolution d'un code, je n'aime vraiment pas retirer des
choses, même des redéfinitions de fonctions publiques.


Bref, une très forte préférence pour l'approche façon /template-
method/ qui me parait mieux respecter le principe /open-close/.

--
Luc Hermitte