design objet et prédicats

Le
meow
Bonjour,

Ne connaissant pas l'existence des unary_function de la stl j'avais
spontanément adopté un design consistant à différencier les prédic=
ats
en fonction de leur domaine d'application en les faisant hériter d'une
classe abstraite représentant le domaine d'application.

exemple :
// abstract class
class PredicateGrooveDefiner {
//! recherche si une cellule est dans un sillon
virtual bool operator()(Cell const&)=0;
}
// later derived in
// a cell is considered as a putative groove if it has a requested
size
PredicateGrooveDefinerVolume:public PredicateGrooveDefiner;
// a cell is considered as a putative groove if it's vertices have a
good curvature index
PredicateGrooveDefinerVerticesCurvature:public PredicateGrooveDefiner;

Je me demandais dans quelle mesure c'est une bonne idée / bonne
pratique. Et maintenant, je me demande s'il ne serait pas possible (et
bon usage) d'hériter en plus de unary_function<Cell,boo>.

Pour la petite histoire, je pense que j'en suis venu à ce "design" à
partir du souvenir d'une observation (de James Kanze si je me souviens
bien) lue ici même il y a quelques années (déjà O_o) et qui disait e=
n
substance qu'il fallait séparer les problèmes et les implémentations.
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Michael DOUBEZ
Le #379549
Bonjour,

Ne connaissant pas l'existence des unary_function de la stl j'avais
spontanément adopté un design consistant à différencier les prédicats
en fonction de leur domaine d'application en les faisant hériter d'une
classe abstraite représentant le domaine d'application.

exemple :
// abstract class
class PredicateGrooveDefiner {
//! recherche si une cellule est dans un sillon
virtual bool operator()(Cell const&)=0;
}
// later derived in
// a cell is considered as a putative groove if it has a requested
size
PredicateGrooveDefinerVolume:public PredicateGrooveDefiner;
// a cell is considered as a putative groove if it's vertices have a
good curvature index
PredicateGrooveDefinerVerticesCurvature:public PredicateGrooveDefiner;

Je me demandais dans quelle mesure c'est une bonne idée / bonne
pratique. Et maintenant, je me demande s'il ne serait pas possible (et
bon usage) d'hériter en plus de unary_function<Cell,boo>.


Ca ne sert pas à grand chose à mon avis. Surtout pour les
unary_function. Pour les binary_function encore, tu peux avoir besoin de
faire un bind sur un des paramètres mais sinon, l'intérèt est limité.

D'un autre coté, ça ne coûte rien de le faire (0.001% de temps à la
compilation au jugé).

Pour la petite histoire, je pense que j'en suis venu à ce "design" à
partir du souvenir d'une observation (de James Kanze si je me souviens
bien) lue ici même il y a quelques années (déjà O_o) et qui disait en
substance qu'il fallait séparer les problèmes et les implémentations.


Tu dois vouloior dire séparer les interfaces et les implémentations.

Utiliser une classe virtuelle pure n'est pas le seul moyen de faire
cette séparation. Tu peux aussi passer par les templates où par un
autre pattern (strategy ...). C'est une question de design.

Michael

meow
Le #379704
Tu dois vouloior dire séparer les interfaces et les implémentations.


Ce que je vais dire ici n'engage que moi (i.e. je ne prétend pas que
c'est ce qu'avait dit James, mais c'est ce que j'en avais compris).
L'idée donc pour illustrer, c'est que tu peux avoir besoin d'une paire
d'entier par exemple pour indexer les éléments d'une matrice, ou pour,
je sais pas moi les cordonnées de points dans une grille de pixels. Et
que pour plus de clarté il valait mieux se fendre de classes ou de
typedefs pour différencier :
typedef pair<int,int> MatriceIndex;
typedef pair<int,int> PointCood;

Et dans mon cas, tu vas peut être pas vouloir mélanger les prédicats
de même signature mais pas de même utilisation. i.e. tu peux avoir un
prédicat qui va te servir à trier les cellules que tu veux aggréger
(première étape de l'algo), et un autre pour les cellules que tu veux
retirer (seconde étape de l'algo).

Utiliser une classe virtuelle pure n'est pas le seul moyen de faire
Si j'ai utilisé de l'abstract pur, c'est que la classe parente ne

saurait être implémentée

cette séparation. Tu peux aussi passer par les templates où par un
autre pattern (strategy ...). C'est une question de design.
Pour être certain de bien te comprendre : pour moi dans le pattern

strategie tu as un objet qui en agrège un autre (la stratégie,
généralement d'un type abstrait (pur ?) d'ailleurs) et lui délègue l e
boulot. Comment tu mettrais ça en pratique dans mon contexte ?

Christophe Lephay
Le #379859
"Michael DOUBEZ" 47a08cd8$0$29185$
Pour la petite histoire, je pense que j'en suis venu à ce "design" à
partir du souvenir d'une observation (de James Kanze si je me souviens
bien) lue ici même il y a quelques années (déjà O_o) et qui disait en
substance qu'il fallait séparer les problèmes et les implémentations.


Tu dois vouloior dire séparer les interfaces et les implémentations.

Utiliser une classe virtuelle pure n'est pas le seul moyen de faire cette
séparation. Tu peux aussi passer par les templates où par un autre pattern
(strategy ...). C'est une question de design.


A mon avis, james parlait surement du pimpl (même si c'est vrai que c'est un
peu pareil que le strategy structurellement parlant)...


meow
Le #379858
A mon avis, james parlait surement du pimpl (même si c'est vrai que c'es t un
peu pareil que le strategy structurellement parlant)...


non, pimpl j'y ai aussi eu droit, mais à un autre moment :)

Pour arrêter de parler dans le vide, j'ai retrouvé le thread en
question :
http://groups.google.com/group/fr.comp.lang.c++/browse_thread/thread/994bd29 5be187f95/a7ddfebbe9f73c01?lnk=gst&q=meow+fichier+lire+points#a7ddfebbe9 f73c01
seconde réponse de James

James Kanze
Le #1065391
On Jan 30, 4:22 pm, meow
Tu dois vouloior dire séparer les interfaces et les implémentations.


Ce que je vais dire ici n'engage que moi (i.e. je ne prétend pas que
c'est ce qu'avait dit James, mais c'est ce que j'en avais compris).
L'idée donc pour illustrer, c'est que tu peux avoir besoin d'une paire
d'entier par exemple pour indexer les éléments d'une matrice, ou pour,
je sais pas moi les cordonnées de points dans une grille de pixels. Et
que pour plus de clarté il valait mieux se fendre de classes ou de
typedefs pour différencier :
typedef pair<int,int> MatriceIndex;
typedef pair<int,int> PointCood;


Sauf que les typedef n'introduisent pas de nouveaux types. Il
faut bien des classes. Tu *peux* les faire, éventuellement, en
dérivant de std::pair (si, comme c'est peut-être le cas ici, les
noms first et second conviennent). Je ne suis pas sûr que ce
soit une bonne idée : pour être utile, il faut que la
dérivation soit publique, et alors, tu cours la risque à ce que
les gens se servent des std::pair<>*, à la place des
MatriceIndex* or des PointCood*. Alors, franchement, je crois
que je me retapperais le code chaque fois.

En fait, ici, ce que tu veux, c'est que la classe MatriceIndex
ait à la fois la même interface et la même implementation d'un
std::pair<int, int>, sans être un std::pair<int, int>. Il n'y a,
actuellement, rien dans le langage qui y convient vraiment. Pour
la même implémentation, ce n'est pas trop difficile : tu peux
hériter de façon privée, ou ta classe peut en contenir une
instance, mais pour l'interface, il n'y a que l'héritage public,
et alors, ton type *est* un std::pair<int, int>. Dans la
pratique, il faut dupliquer l'interface. (Et dans le cas de
std::pair<>, l'implémentation est l'interface -- il n'y a
vraiment rien d'autre.)

(Il me semble avoir entendu parler d'un nouveau type de typedef
dans la prochaine version de la norme, qui fera ce que tu veux.
Il faut dire que tu n'es pas le premier à en avoir besoin.)

Et dans mon cas, tu vas peut être pas vouloir mélanger les
prédicats de même signature mais pas de même utilisation. i.e.
tu peux avoir un prédicat qui va te servir à trier les
cellules que tu veux aggréger (première étape de l'algo), et
un autre pour les cellules que tu veux retirer (seconde étape
de l'algo).


Attention : les prédicats servent à l'instantiation des
templates. Et le typage, dans les templates, c'est ce qu'on
appelle le « duck typing » (d'après un dicton anglais -- de
Groucho Marx, je crois -- : « If it walks like a duck and
quacks like a duck, it is a duck »). En somme, si un objet a un
type qui peut être appelé (comme une fonction) avec un paramètre
du type en question, et qui renvoie quelque chose qui peut
servir de bool, c'est un prédicat. Si tu veux distinguer entre
des prédicats sur MatriceIndex et des prédicats sur PointCood,
par exemple, il faut bien que les deux soient des classes
distinctes. Et si tes deux prédicats, comme il me semble être le
cas ci-dessus, travaillent en fait sur le même type, tu ne
pourrais pas les distinguer.

Au moins dans la philosophie de la STL. Si tu implémentes tes
propres algorithmes, rien ne t'empêche de faire mieux ; de
créer des fonctions qui prenent des interfaces (par référence,
puisque le polymorphisme ne fonctionne pas avec la copie)
Aggregeable ou Retirable. C'est à la fois plus sûr et plus
flexible.

Utiliser une classe virtuelle pure n'est pas le seul moyen
de faire


Si j'ai utilisé de l'abstract pur, c'est que la classe parente
ne saurait être implémentée

cette séparation. Tu peux aussi passer par les templates où
par un autre pattern (strategy ...). C'est une question de
design.


Pour être certain de bien te comprendre : pour moi dans le
pattern strategie tu as un objet qui en agrège un autre (la
stratégie, généralement d'un type abstrait (pur ?) d'ailleurs)
et lui délègue le boulot. Comment tu mettrais ça en pratique
dans mon contexte ?


Je crois qu'il a étendu le concepte de « stratégie » aux
fonctions. À la place d'une classe qui utilise un délégué,
c'est une fonction qui l'utilise.

--
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


Michael DOUBEZ
Le #1065390
Tu dois vouloior dire séparer les interfaces et les implémentations.


Ce que je vais dire ici n'engage que moi (i.e. je ne prétend pas que
c'est ce qu'avait dit James, mais c'est ce que j'en avais compris).
L'idée donc pour illustrer, c'est que tu peux avoir besoin d'une paire
d'entier par exemple pour indexer les éléments d'une matrice, ou pour,
je sais pas moi les cordonnées de points dans une grille de pixels. Et
que pour plus de clarté il valait mieux se fendre de classes ou de
typedefs pour différencier :
typedef pair<int,int> MatriceIndex;
typedef pair<int,int> PointCood;


Non seulement ça permet de différencier mais ça rends les programmes
plus lisibles. Et puis si tu décide de changer la forme, ça évite du
travail.

MatriceIndex i;
est AMA plus lisible et plus facile et ça éloigne la tentation de
réutiliser la locale pour une autre sémantique.
pair<int,int> i; //matrice index
// mat[i]...
//from now on, i is a point coordinate
// Point=i;


Et dans mon cas, tu vas peut être pas vouloir mélanger les prédicats
de même signature mais pas de même utilisation. i.e. tu peux avoir un
prédicat qui va te servir à trier les cellules que tu veux aggréger
(première étape de l'algo), et un autre pour les cellules que tu veux
retirer (seconde étape de l'algo).

Utiliser une classe virtuelle pure n'est pas le seul moyen de faire
Si j'ai utilisé de l'abstract pur, c'est que la classe parente ne

saurait être implémentée


Ce que je veux dire c'est que tes prédicats n'ont pas forcémemnt à avoir
une base commune pour réaliser un concept commun.

cette séparation. Tu peux aussi passer par les templates où par un
autre pattern (strategy ...). C'est une question de design.
Pour être certain de bien te comprendre : pour moi dans le pattern

strategie tu as un objet qui en agrège un autre (la stratégie,
généralement d'un type abstrait (pur ?) d'ailleurs) et lui délègue le
boulot. Comment tu mettrais ça en pratique dans mon contexte ?



Je ne connais pas ton contexte:
* un système par stratégie serait adapté si tu as des prédicats
polymorphes.
* un système de classes avec une base commune si tu as une méthode
opaque pour parcourir tes éléments ou si tu as besoin d'une factory en
fonction de de l'objet sur lequel tu travailles.
* un système de template pour pouvoir composer les types de prédicat.

Et ça peut être une composition de tout cela pour offrir une
présentation syntaxique de ton domaine d'application proche d'un DSL.

Un example pourrait être que tu as besoin d'utiliser les predicats de la
STL (greater,not1, equal, boost::bind ...) mais tout en conservant ta
classe class PredicateGrooveDefiner parce que tu as une méthode opaque.
void agregate(CellContainer& v,cons PredicateGrooveDefiner& pred);

Dans ce cas, tu peux définir:

//compose a predicate in PredicateGrooveDefiner interface
template <typename PRED>
struct PredicateGrooveDefinerBuilder: public PredicateGrooveDefiner
{
PredicateGrooveDefinerBuilder(const PRED& p):predicate_(p){}

virtual bool operator()(Cell const& c)const
{
return predicate_(c);
}

private:
PRED predicate_;
};

template <typename PRED>
PredicateGrooveDefinerBuilder<PRED> make_PredicateGrooveDefiner(const
PRED& p)
{
return PredicateGrooveDefinerBuilder<PRED>(p);
}

Ensuite tu définis tes accesseurs et prédicats sans t'occuper de
PredicateGrooveDefinerBuilder et tu appelles:
agregate(v,make_PredicateGrooveDefiner(my_predicate));

Mais si tu as un autre design, qui définit:

template <typename PRED>
void agregate(CellContainer& v,cons PRED& pred);

tu peux directement appeler:
agregate(v, my_predicate);


D'une manière générale, si tu arrives à implémenter le concept des
iterators pour ton CellContainer, alors tu peux utiliser les algorithmes
de la STL et ça fait gagner en temps et en qualité.

Michael


Publicité
Poster une réponse
Anonyme