OVH Cloud OVH Cloud

Facade, interface publique et interface "privee"

10 réponses
Avatar
Jean-Marc Bourguet
J'ai une classe qui est une Facade vers un sous systeme. Elle a donc
en membres publics les fonctions d'interfacage prevue pour etre
utilisee a l'exterieur du sous-systeme.

Le probleme est que les autres classes du sous-systeme ont besoin
d'acceder a des fonctions qui ne doivent etre utilisee que par le
sous-systeme.

Solutions possibles:

o mettre toute ces autres classes en friend de la classe Facade; ca
fait beaucoup d'amis, ca force certaines operations qui peuvent etre
implementee dans des namespaces prives a etre membre de ces classes
ou des fonctions publiques pour que ces fonctions soient friend, ca
empeche d'avoir des vraies fonctions privees a la Facade.

o faire une classe (friend de Facade, donc ce ne peut pas etre un
namespace) qui n'a que des membres statiques fournissant l'interface
supplementaires accessibles aux composants du sous-systeme.
L'indirection me gene.

o rendre protected l'interface "privee", faire une classe descendant
de Facade qui a des membres publics qui renvoient a cette interface
privee. A nouveau l'indirection me gene et la maniere d'avoir acces
a la classe fournissant l'interface privee n'est pas claire (ok, un
cast du pointeur fonctionne si on s'arrange pour ne construire que
des objects de cette classe, mais bon).


Quelqu'un a-t'il d'autres alternatives?

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

10 réponses

Avatar
Cédric LEMAIRE
o mettre toute ces autres classes en friend de la classe Facade; ca
fait beaucoup d'amis, ca force certaines operations qui peuvent etre
implementee dans des namespaces prives a etre membre de ces classes
ou des fonctions publiques pour que ces fonctions soient friend, ca
empeche d'avoir des vraies fonctions privees a la Facade.
Il manque en C++ la possibilité de réduire la portée du 'friend' à

un bloc d'accès public/protected/private. J'en souffre
régulièrement, lorsqu'il s'agit justement, au sein d'une même
classe, de mêler parties "fonctionnelles" (service exposé) et parties
"techniques" (collaboration avec le framework de l'application).
Comme je ne veux pas m'embarrasser d'une complexité supplémentaire
dans mon diagramme de classe, uniquement parce qu'il manque cette
portée réduite, j'emploie cette solution, MAIS en plaçant le
'friend' à la fin du bloc protected/private de déclaration des
membres qui le concernent. Je fais donc autant de blocs
private/protected que j'ai de portées de 'friend' différentes.
C'est purement visuel et malheureusement pas contrôlé par le
compilateur.

J'avais suggéré il y a quelques temps dans je ne sais plus quel
newsgroup sur le C++, l'idée que réduire la portée des 'friends'
pourrait être utile. Cela n'avait pas été compris, et l'on me
proposait plutôt de monter des usines à gaz pour quelque chose qui
devrait rester simple à exprimer.

o faire une classe (friend de Facade, donc ce ne peut pas etre un
namespace) qui n'a que des membres statiques fournissant l'interface
supplementaires accessibles aux composants du sous-systeme.
L'indirection me gene.
Pourquoi des membres statiques? Votre classe friend ne pourrait-elle

pas encapsuler l'instance de la façade?
Pour moi, ce n'est pas l'indirection qui gêne, mais plutôt la
nécessité de créer une classe de plomberie pour contourner une
fonctionnalité manquante peu couteuse du C++.
En utilisant un outil de parsing et de génération de code comme
CodeWorker (http://www.codeworker.org), il serait facile de générer
l'indirection et de maintenir sa mise à jour automatiquement en cas de
changement de l'interface accessible aux composants du sous-système.

o rendre protected l'interface "privee", faire une classe descendant
de Facade qui a des membres publics qui renvoient a cette interface
privee. A nouveau l'indirection me gene et la maniere d'avoir acces
a la classe fournissant l'interface privee n'est pas claire (ok, un
cast du pointeur fonctionne si on s'arrange pour ne construire que
des objects de cette classe, mais bon).
Il faut instancer la sous-classe de la façade. L'existence de cette

sous-classe ne doit pas être connue de l'extérieur, par les
utilisateurs des services de la façade (on n'expose pas le header
C++). Un peu selon le principe d'une interface. D'ailleurs, il faudrait
interdire l'instanciation de la classe mère de la façade, pour plus
de précaution (rendre tous les constructeurs protected).
Au sein des composants du système, il suffira donc de dynamic-caster
la façade en sa sous-classe pour travailler dessus.
Pour la redirection, l'automatiser comme pour la solution 2.

Personnellement, je retiendrais les solutions 1 et 3.
L'inconvénient de la première est que les 'friends' étendus à
l'ensemble des membres sont une insulte à une belle conception, et
dénaturent l'usage des accès réglementés protected/private... En
attendant que le C++ offre la portée réduite? Ce n'est pas la
solution à retenir sur un projet en équipe, à mon avis (propreté +
élégance sont importants pour bien supporter les futures
évolutions/refactorings du code, faits par des développeurs qui
ignoreront peut-être la présence/signification/origine de ces
'friends').
L'inconvénient de la dernière solution est qu'elle oblige à créer
une classe supplémentaire, conduisant à maintenir la redirection des
membres privés. Sachant que l'écriture de la redirection peut
s'automatiser, cette solution reste la plus élégante : on expose
quelque chose qui ressemble à une interface, et on peut le manipuler
au sein du framework. Je vote pour!

Cédric

Avatar
Arnaud Meurgues
Jean-Marc Bourguet wrote:

J'ai une classe qui est une Facade vers un sous systeme. Elle a donc
en membres publics les fonctions d'interfacage prevue pour etre
utilisee a l'exterieur du sous-systeme.

Le probleme est que les autres classes du sous-systeme ont besoin
d'acceder a des fonctions qui ne doivent etre utilisee que par le
sous-systeme.

Solutions possibles:

o mettre toute ces autres classes en friend de la classe Facade; ca
fait beaucoup d'amis, ca force certaines operations qui peuvent etre
implementee dans des namespaces prives a etre membre de ces classes
ou des fonctions publiques pour que ces fonctions soient friend, ca
empeche d'avoir des vraies fonctions privees a la Facade.

o faire une classe (friend de Facade, donc ce ne peut pas etre un
namespace) qui n'a que des membres statiques fournissant l'interface
supplementaires accessibles aux composants du sous-systeme.
L'indirection me gene.

o rendre protected l'interface "privee", faire une classe descendant
de Facade qui a des membres publics qui renvoient a cette interface
privee. A nouveau l'indirection me gene et la maniere d'avoir acces
a la classe fournissant l'interface privee n'est pas claire (ok, un
cast du pointeur fonctionne si on s'arrange pour ne construire que
des objects de cette classe, mais bon).


Quelqu'un a-t'il d'autres alternatives?


Si j'ai bien compris le problème, j'aurais bien vu quelque chose du genre :
- une classe façade contenant toutes les fonctions nécessaires à tout
le monde (externe et interne) ;
- une classe façade cliente de la première qui réduit l'interface pour
l'extérieur
En gros, c'est le contraire de ta deuxième solution, mais sans avoir
besoin de friend.

Mais je n'ai peut-être pas bien compris le problème...

--
Arnaud

Avatar
Jean-Marc Bourguet
Arnaud Meurgues writes:

Mais je n'ai peut-être pas bien compris le problème...


Non, c'est bien ca. C'est aussi une solution. (Tout comme d'ailleurs
une reanalyze de la Facade pour voir si c'est bien necessaire.).

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
Cédric LEMAIRE
Si j'ai bien compris le problème, j'aurais bien vu quelque chose du gen re :
- une classe façade contenant toutes les fonctions nécessaires à tout
le monde (externe et interne) ;
- une classe façade cliente de la première qui réduit l'interface pour
l'extérieur
En gros, c'est le contraire de ta deuxième solution, mais sans avoir
besoin de friend.

Mais je n'ai peut-être pas bien compris le problème...
La troisième solution proposée ne requiérait pas non plus de

'friend'.

Finalement, j'ai une préférence pour une cinquième solution : classe
interface de la façade avec uniquement les membres publiques; classe
d'implémentation héritant de la façade, et implémentant les membres
publics + privés (ces derniers ne sont visibles qu'à ce niveau). Les
sous-composants doivent dynamic-caster dans la classe
d'implémentation, pour bénéficier de l'interface "privée" (non
exposée aux utilisateurs de la façade).
Pas de redirection. Pas de 'friend' non plus.

N.B. : on arrive toujours à se passer de 'friend' (contre-exemples?),
mais au prix de plus de code à taper et dont il faut maintenir la
cohérence. Ce qui n'est pas toujours souhaitable.

Avatar
Jean-Marc Bourguet
"Cédric LEMAIRE" writes:

Si j'ai bien compris le problème, j'aurais bien vu quelque chose du genre :
- une classe façade contenant toutes les fonctions nécessaires à tout
le monde (externe et interne) ;
- une classe façade cliente de la première qui réduit l'interface pour
l'extérieur
En gros, c'est le contraire de ta deuxième solution, mais sans avoir
besoin de friend.

Mais je n'ai peut-être pas bien compris le problème...
La troisième solution proposée ne requiérait pas non plus de

'friend'.

Finalement, j'ai une préférence pour une cinquième solution : classe
interface de la façade avec uniquement les membres publiques; classe
d'implémentation héritant de la façade, et implémentant les membres
publics + privés (ces derniers ne sont visibles qu'à ce niveau). Les
sous-composants doivent dynamic-caster dans la classe
d'implémentation, pour bénéficier de l'interface "privée" (non
exposée aux utilisateurs de la façade).
Pas de redirection. Pas de 'friend' non plus.


Mais la facade se met a comporter une tonne de membres virtuels (alors
qu'elle n'en a aucun pour le moment).

--
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
Cédric LEMAIRE
Finalement, j'ai une préférence pour une cinquième solution : cla sse
interface de la façade avec uniquement les membres publiques; classe
d'implémentation héritant de la façade, et implémentant les mem bres
publics + privés (ces derniers ne sont visibles qu'à ce niveau). Les
sous-composants doivent dynamic-caster dans la classe
d'implémentation, pour bénéficier de l'interface "privée" (non
exposée aux utilisateurs de la façade).
Pas de redirection. Pas de 'friend' non plus.


Mais la facade se met a comporter une tonne de membres virtuels (alors
qu'elle n'en a aucun pour le moment).
Effectivement. Des membres virtuels purs. C'est gênant?



Avatar
Jean-Marc Bourguet
"Cédric LEMAIRE" writes:

Finalement, j'ai une préférence pour une cinquième solution : classe
interface de la façade avec uniquement les membres publiques; classe
d'implémentation héritant de la façade, et implémentant les membres
publics + privés (ces derniers ne sont visibles qu'à ce niveau). Les
sous-composants doivent dynamic-caster dans la classe
d'implémentation, pour bénéficier de l'interface "privée" (non
exposée aux utilisateurs de la façade).
Pas de redirection. Pas de 'friend' non plus.


Mais la facade se met a comporter une tonne de membres virtuels (alors
qu'elle n'en a aucun pour le moment).
Effectivement. Des membres virtuels purs. C'est gênant?



C'est un point a evaluer et a prendre en compte. Pas sur que ce le
soit.

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
Cédric LEMAIRE
Mais la facade se met a comporter une tonne de membres virtuels (alors
qu'elle n'en a aucun pour le moment).
Effectivement. Des membres virtuels purs. C'est gênant?



C'est un point a evaluer et a prendre en compte. Pas sur que ce le
soit.
Si cela vient du fait de devoir maintenir l'écriture d'un membre

virtuelle pure pour chaque membre public de la façade, j'insiste à
nouveau : c'est 15 lignes de script CodeWorker de générer les
fonctions virtuelles pures à partir du header C++ de
l'implémentation... qui pourra donc changer comme bon lui semble,
puisqu'il n'y aura aucun impact développeur pour maintenir
l'interface.

Si la solution vous intéresse, je publie le script ici même.



Avatar
Jean-Marc Bourguet
"Cédric LEMAIRE" writes:

Mais la facade se met a comporter une tonne de membres virtuels (alors
qu'elle n'en a aucun pour le moment).
Effectivement. Des membres virtuels purs. C'est gênant?



C'est un point a evaluer et a prendre en compte. Pas sur que ce le
soit.
Si cela vient du fait de devoir maintenir l'écriture d'un membre

virtuelle pure pour chaque membre public de la façade,


C'est pas ca (c'est pas plus complique que d'ecrire des wrappers dans
l'autre sens).

j'insiste à nouveau : c'est 15 lignes de script CodeWorker de
générer les fonctions virtuelles pures à partir du header C++ de
l'implémentation... qui pourra donc changer comme bon lui semble,
puisqu'il n'y aura aucun impact développeur pour maintenir
l'interface.

Si la solution vous intéresse, je publie le script ici même.


Non. Il faudrait:
- evaluer CodeWorker pour se convaincre que c'est la bonne solution
- convaincre tout le monde que c'est la bonne solution (y compris
le departement legal)
- mettre CW dans le systeme de build et le faire fonctionner
pour l'ensemble des platerformes cibles.
- reussir a faire ca dans les temps pour la prochaine release

Cout/benefice trop petit, meme pas envisage.

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
Cédric LEMAIRE
Si cela vient du fait de devoir maintenir l'écriture d'un membre
virtuelle pure pour chaque membre public de la façade,


C'est pas ca (c'est pas plus complique que d'ecrire des wrappers dans
l'autre sens).
Il ne s'agit pas de considérer le degré de complexité, mais plus la

pénibilité, s'il y a vraiment une tonne de fonctions membres.

c'est 15 lignes de script CodeWorker de
générer les fonctions virtuelles pures à partir du header C++ de
l'implémentation...


Non. Il faudrait:
- evaluer CodeWorker pour se convaincre que c'est la bonne solution
Si c'est pour une utilisation one-shot (traiter la tonne de fonctions

une seule fois, puis jeter), l'évaluation se borne à regarder si
"Facade.h" compile lorsqu'il hérite de "IFacade.h":
codeworker -translate le_script_gentiment_propose.cwp Facade.h
IFacade.h

- convaincre tout le monde que c'est la bonne solution (y compris
le departement legal)
Si c'est pour une utilisation one-shot, CodeWorker tire sa révérence

une fois "IFacade.h" écrit. Au résultat, cela peut être n'importe
quel développeur qui aurait pu l'écrire. Sauf que pas de sueur.

- mettre CW dans le systeme de build et le faire fonctionner
pour l'ensemble des platerformes cibles.
Si c'est pour une utilisation one-shot, inutile si "IFacade.h" se

retrouve à l'identique sur toutes les plateformes.
- reussir a faire ca dans les temps pour la prochaine release
Etapes :

- télécharger CodeWorker (sous Windows, il y a déjà le binaire),
- taper la ligne de commande ci-dessus,
- insérer à la main les #include + forward declarations
nécessaires à "IFacade.h",
- recompiler sur les différentes plateformes.
Il faudrait que la prochaine release ne soit pas pour dans 10 minutes!

Cout/benefice trop petit, meme pas envisage.
Même au regard de ces dernières précisions?