Pattern Visiteur et Decorateur

Pattern Visiteur et Decorateur - C#/.NET managed - Programmation

Marsh Posté le 17-05-2007 à 12:49:09    

Bonjour,
 
j'utilise actuellement une bibliotheque qui a une fonction qui me renvoie une liste d'objets (XObj1, XObj2 qui derivent tous de XObj).
 
J'aimerais afficher cette liste en faisant un traitement different en fonction du type d'objet.
 
Pour cela, je souhaiterais utiliser un visiteur qui me parait la solution la plus souple.
 
Malheureusement, je ne peux modifier le code des objetx Xobj, Xobj1 Xobj2.
J'ai donc pensé a faire un decorateur qui dont voici le code :
 
class XObjectDecorator
{
private XObj _wow = null;
 
public XObjectDecorator(XObj w)
{
this._wow = w;
}
 
public void Accept(IVisitor v)
{
v.Visit(this._wow);
}
}
 
que j'utiliserais comme ceci :
ConcreteVisitor visit = new ConcreteVisitor();
new XObjectDecorator(gList.Object(i)).Accept(visit);
 
Malheureusement, a la compilation j'ai l'erreur suivant sur v.visit(this._wow) :
 
Erreur 1 La méthode surchargée correspondant le mieux à 'IVisitor.Visit(XObj1)' possède des arguments non valides
Erreur 2 Argument '1' : impossible de convertir de 'XObj' en 'XObj1'
 
Je ne vois pas du tout ce qui ne va pas.
 
Auriez-vous une idee ?
 
merci d'avance
 
v.

Reply

Marsh Posté le 17-05-2007 à 12:49:09   

Reply

Marsh Posté le 18-05-2007 à 01:08:24    

J'ai rien pigé [:cerveau foudtag]
 
C'est quoi ces histoires de visiteurs et décorateur ?
Et pourquoi t'as un visiteur en béton ? (concrete chez moi ça veut dire béton en anglais :o)

Reply

Marsh Posté le 18-05-2007 à 01:14:41    

MagicBuzz a écrit :

J'ai rien pigé [:cerveau foudtag]

 

C'est quoi ces histoires de visiteurs et décorateur ?
Et pourquoi t'as un visiteur en béton ? (concrete chez moi ça veut dire béton en anglais :o)


 [:totoz]

 

-------

 

IVisitor à une méthode Visit(XObj) ? (w doi être XObj et pas XObj1 ou 2 non ?) (verifie via le debugueur - bref, c'est quoi les types des objets qui sont dans ta listes)

 

Par contre, ce qui m'étonne, c'est que ton décorateur n'hérite pas de XObj (et en quoi il décore, en fait ?)

 


(je supose que XObj1 et 2 hérite de XObj)

  


Message édité par zapan666 le 18-05-2007 à 01:26:04

---------------
my flick r - Just Tab it !
Reply

Marsh Posté le 18-05-2007 à 02:41:44    

MagicBuzz a écrit :

J'ai rien pigé [:cerveau foudtag]
 
C'est quoi ces histoires de visiteurs et décorateur ?
Et pourquoi t'as un visiteur en béton ? (concrete chez moi ça veut dire béton en anglais :o)


je pense que la lecture de ce bouquin s'impose : http://www.amazon.fr/Design-patter [...] 2841773507

Reply

Marsh Posté le 18-05-2007 à 10:23:07    

Y'a une version PDF gratuite ?

Reply

Marsh Posté le 18-05-2007 à 11:06:21    

Ben si ça va pas chercher plus loin que ça, y'a pas besoin d'acheter un bouquin à 56 € pour savoir ce que c'est.
http://en.wikipedia.org/wiki/Visitor_pattern
http://en.wikipedia.org/wiki/Double_dispatch
http://www.exciton.cs.rice.edu/Jav [...] attern.htm
 
http://nice.sourceforge.net/visitor.html
=> Le dernier lien semble contredire la conclusion du second non ? Lequel est dans le vrai dans cas du C# ? Notamment, l'exemple de l'utilité du double dispatching s'applique au C++ qui est présenté dans l'exemple ayant des lacunes du point de vue de la gestion des surcharges. Qu'en est-il avec le C# ? Parceque moi je trouve aussi que simplement dériver tous mes objets de la même interface est une solution plus propre et surtout, moins chiante à coder/relire (le code est dans la classe, pas dans une classe qui n'a rien à voir et qu'on n'atteind qu'à force d'appels récursifs)

Message cité 1 fois
Message édité par MagicBuzz le 18-05-2007 à 11:08:17
Reply

Marsh Posté le 18-05-2007 à 11:50:24    

MagicBuzz a écrit :

(le code est dans la classe, pas dans une classe qui n'a rien à voir et qu'on n'atteind qu'à force d'appels récursifs)


Si ta modélisation est mal foutu, forcement, ça n'aide pas.
si tu attaque la classe parente de ta classe parente, il y a un problème.


---------------
my flick r - Just Tab it !
Reply

Marsh Posté le 18-05-2007 à 12:07:02    

:heink:
 
Ben non. Regarde l'exemple du premier lien.
 
Le code relatif à l'affichage de "Car", "Wheel" et autres se trouve non pas dans les classes en question, mais dans le visiteur (PrintVisitor) au lieu d'être dans chacune des classes.
 
Pour moi, il serait plus judicieux de faire une interface IPrintable, contenant "Print()" et en faire hériter toutes les classes en question.
=> Ainsi, le code relatif à l'impression sont directement dans les objets plutôt que dans un objet qui y fait référence, et qui est appelé par ces objets en question.
 
Bon, l'exemple est très succint, et il y a certainement des cas où l'intérêt est réel, mais là je vois pas trop.
 
Le seul intérêt à mes yeux est celui annoncé au départ : si on veut modifier le comportement de "Print()" on peut n'a pas besoin de modifier les objets "parents", tout le code relatif à l'impression se trouve dans la même classe. Mais bon, avec la clause "partial", on peut parfaitement en C# regrouper tout le code relatif à l'impression de tous les objets dans un fichier dédié, donc ensuite l'intérêt me semble assez maigre.
 
Le second intérêt (mais est-ce un intérêt) c'est que les objets "parents" ne fond plus de références croisés entre eux, c'est la class visteur qui s'en charge. Mais à nouveau, à quoi ça sert ? (dans l'exemple de la classe Car, il fait de toute façon déjà références aux autres objets...)

Message cité 1 fois
Message édité par MagicBuzz le 18-05-2007 à 12:12:10
Reply

Marsh Posté le 18-05-2007 à 12:16:11    


non mais en fait, j'ai compris que tu faisais quelque chose genre

Code :
  1. super.super.super.super.super.doIt()


 J'ai eux peur [:cupra]  
 

MagicBuzz a écrit :


Le code relatif à l'affichage de "Car", "Wheel" et autres se trouve non pas dans les classes en question, mais dans le visiteur (PrintVisitor) au lieu d'être dans chacune des classes.


Si il veut changer la méthode d'affichage des objets (en envoyant tout dans la sortie d'erreur par exemple), il n'a que le visiteur à modifier et pas toutes les classes Car, Wheel, etc.
 
Et en allant plus loin, si la personne implemente un adaptateur dans le visiteur, selon que tu sois en Debug ou Release par exemple, le programme peut sortir les informations soit sur la sortie standard (debug) soit dans un buffer (Release par exemple) ce qui n'est pas possible si tu mets ce code dans les classes Car, etc à moins de forcer ça quelque part.
 
(l'adaptateur va faire le choix selon la situation Debug ou Release)


---------------
my flick r - Just Tab it !
Reply

Marsh Posté le 18-05-2007 à 12:26:14    

Ben moi dans ces cas, je fais une classe "Afficheur", et c'est lui qui s'occupe de faire ça proprement. Mais c'est les objets eux-même qui lui disent quoi afficher.
 
Ceci dit, je comprends parfaitement ton explication en ce qui concerne un changement de comportement de l'affichage pour toutes les méthodes d'un coup.
Mais dans ce cas, je te renvois à la clause "partial", qui permet de pallier à un certain nombre de problèmes sans problème.
 
(Bon, pas complètement, genre si "Visitor" contiens aussi des méthodes privées partagées entre tous les Print(), je suis d'accord que là c'est baisé si tu ne passes pas par cette structure :jap:

Reply

Marsh Posté le 18-05-2007 à 12:26:14   

Reply

Marsh Posté le 18-05-2007 à 12:30:52    

MagicBuzz a écrit :


Mais dans ce cas, je te renvois à la clause "partial", qui permet de pallier à un certain nombre de problèmes sans problème.


 [:cupra] J'ai jamais fais de C#


---------------
my flick r - Just Tab it !
Reply

Marsh Posté le 18-05-2007 à 13:22:36    

Ben partial, ça sert juste à dire "ce petit bout de code en fait, c'est un morceau de ma classe untel". Je pense qu'on a la même chose en Java.
 
Exemple :
 
interfaces.cs

Code :
  1. namespace TstPrint
  2. {
  3.    public interface IPrintable
  4.    {
  5.        void Print();
  6.    }
  7.  
  8.    public interface ITruc
  9.    {
  10.        void Truc();
  11.    }
  12. }


 
voiture.cs

Code :
  1. namespace TstPrint
  2. {
  3.    public partial class Voiture : IPrintable, ITruc
  4.    {
  5.        private string nom;
  6.        private Roue[] roues;
  7.        private Couleur couleur;
  8.        private IPrintable[] allPrintableMembers = new IPrintable[5];
  9.        
  10.        public Voiture(string Nom, float RadiusRoues)
  11.        {
  12.            this.nom = Nom;
  13.            this.couleur = new Couleur("Verte" );
  14.            this.allPrintableMembers[4] = this.couleur;
  15.            this.roues = new Roue[4];
  16.            for (int i = 0; i < 4; i++)
  17.            {
  18.                this.roues[i] = new Roue(i, RadiusRoues);
  19.                this.allPrintableMembers[i] = this.roues[i];
  20.            }
  21.        }
  22.  
  23.        public string GetName()
  24.        {
  25.            return this.nom;
  26.        }
  27.    }
  28. }


 
Roue.cs

Code :
  1. namespace TstPrint
  2. {
  3.    public partial class Roue : IPrintable, ITruc
  4.    {
  5.        private int position;
  6.        private float radius;
  7.  
  8.        public Roue(int Position, float Radius)
  9.        {
  10.            this.position = Position;
  11.            this.radius = Radius;
  12.        }
  13.  
  14.        public float GetRadius()
  15.        {
  16.            return this.radius;
  17.        }
  18.    }
  19. }


 
couleur.cs

Code :
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4.  
  5. namespace TstPrint
  6. {
  7.    public partial class Couleur : IPrintable
  8.    {
  9.        string couleur;
  10.  
  11.        public Couleur(string Color)
  12.        {
  13.            this.couleur = Color;
  14.        }
  15.  
  16.        public string GetCouleur()
  17.        {
  18.            return couleur;
  19.        }
  20.    }
  21. }


 
print.cs

Code :
  1. using System;
  2.  
  3. namespace TstPrint
  4. {
  5.    public partial class Voiture
  6.    {
  7.        public void Print()
  8.        {
  9.            Console.WriteLine("Voiture : " + this.GetName());
  10.  
  11.            foreach (IPrintable printableObject in this.allPrintableMembers)
  12.            {
  13.                printableObject.Print();
  14.            }
  15.        }
  16.    }
  17.  
  18.    public partial class Roue
  19.    {
  20.        public void Print()
  21.        {
  22.            Console.WriteLine("Roue " + this.position.ToString() + " : " + this.GetRadius() + " pouces" );
  23.        }
  24.    }
  25.  
  26.    public partial class Couleur
  27.    {
  28.        public void Print()
  29.        {
  30.            Console.WriteLine("Couleur : " + this.GetCouleur()););
  31.        }
  32.    }
  33. }


 
truc.cs

Code :
  1. namespace TstPrint
  2. {
  3.     public partial class Voiture
  4.     {
  5.         public void Truc()
  6.         {
  7.             this.nom = this.nom.Substring(0, 3);
  8.             for (int i = 0; i < 4; i++)
  9.             {
  10.                 this.roues[i].Truc();
  11.             }
  12.         }
  13.     }
  14.     public partial class Roue
  15.     {
  16.         public void Truc()
  17.         {
  18.             this.radius /= 2f;
  19.         }
  20.     }
  21. }


 
program.cs

Code :
  1. using System;
  2.  
  3. namespace TstPrint
  4. {
  5.    class Program
  6.    {
  7.        static void Main(string[] args)
  8.        {
  9.            Voiture v = new Voiture("Laguna", 8.2f);
  10.            v.Print();
  11.            Console.WriteLine("-----------" );
  12.            v.Truc();
  13.            v.Print();
  14.            Console.ReadKey();
  15.        }
  16.    }
  17. }


 
Sortie :


Voiture : Laguna
Roue 0 : 8,2 pouces
Roue 1 : 8,2 pouces
Roue 2 : 8,2 pouces
Roue 3 : 8,2 pouces
Couleur : Verte
-----------
Voiture : Lag
Roue 0 : 4,1 pouces
Roue 1 : 4,1 pouces
Roue 2 : 4,1 pouces
Roue 3 : 4,1 pouces
Couleur : Verte


 
=> On voit dans cet exemple que tout ce qui est relatif à l'impression se situe dans le fichier "print.cs", alors que les méthodes d'affichage restent bel et bien dans les objets finaux.
 
-- Edit : Version corrigée avec un exemple qui marche ;)
-- Edit² : Et plus complet :D


Message édité par MagicBuzz le 18-05-2007 à 13:50:08
Reply

Marsh Posté le 19-05-2007 à 20:42:46    

alors la ca m'interesse.
je ne connaissais pas le mot cle partial (c# 2 ?)
puis-je de cette maniere completer une classe que je n'ai pas ecrite ?
cad une classe qui se trouverait dans un framework qualconque ?
 
si oui, alors il me suffit de rajouter une methode d'acceptation de mon visiteur dans les classes issues de la bibliotheque que j'utilise (un decorateur qui ne dis pas son nom quoi !)
 
Edit:
 
bon a priori je me suis affole pour rien, ca ne fonctionne pas.
etant donne que je n'ai pas la main sur les objets de la bibliotheque je ne peux pas leur rajouter de comportement en utilisant partial.


Message édité par vonm le 19-05-2007 à 20:49:57
Reply

Marsh Posté le 20-05-2007 à 11:38:33    

il faut que les classes initiales soit dotée du mot clé partial.  
 
Maintenant pour en revenir à son utilisation, ça doit être utilisé pour séparer le code d'une classe dans plusieurs fichiers. Si c'est pour complété tes objets, autant en dérivé et complété dans la classe fille.  
 
oui PARTIAL est arrivé avec .NET 2.0

Reply

Marsh Posté le 21-05-2007 à 09:27:06    

Je tiens à souligner que si "partial" part d'une très bonne idée, on remarque dans mon exemple plusieurs choses :
- Pour indiquer de quoi dérive une classe, il suffit d'indiquer les classes "parentes" dans un seul fichier. Je n'ai pas testé les mélanges, mais avec un peu de pas de chance, ça marche (genre dans un fichier, je dérive d'une interface, et dans un autre fichier, d'une autre). Dans ce cas, on remarque rapidement les problèmes de maintenance que cela peut impliquer...
- Partial peut être utilisé comme dans mon exemple, pour regrouper dans un même fichier toutes les surcharges de différentes classes dérivées d'une même interface, de façon à retrouver facilement le code. Le problème, c'est que rapidement ça peut se bordeliser, si une personne qui intervient sur le projet ne suit pas cette façon de "ranger" le code. Idem si un fichier servait à contenir les méthodes "métier" d'une classe. Si on ne suis pas la règle, on peut rapidement arriver à du code difficile à relire.
- L'implémentation "de base" en C# consiste à mettre dans un fichier spécial toute "l'initialisation" et "le design" des form dans un fichier, tandis que le code utilisateur se trouve dans un autre fichier. A nouveau, si le code "utilisateur" se met à faire de l'inisitlisation -notamment, on y trouve le constructeur- ou du design (présente de la méthode onpaint par exemple) on peut arriver à une lisibilitée très amoindrie : mais d'où vient cet objet ? il est initialisé comment ? et qu'est-ce qu'il fout ici au milieu de ma form ? etc.
 
Bref, à utiliser en faisant attention. En soit il peut être très pratique et utile, mais aussi s'avérer l'enemi juré de la maintenabilité du code, surtout si on n'utilise pas de GUI évoluée.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed