2009-11-08 4 views
2

Est-ce que le code ci-dessous sent? Je refactorise du code et ai découvert cette relation circulaire où foo a besoin d'une classe qui a besoin d'une interface que foo elle-même implémente.Utilisation d'une classe dans son constructeur C# - Est-ce qu'elle sent?

Dans le code réel, foo est un UserControl Silverlight et ifoo a des méthodes pour faire des choses de type UI comme lever une boîte de dialogue (par exemple ShowMessage). La classe needsAnIfoo est un (type de) contrôleur qui utilise l'interface ifoo chaque fois qu'il veut faire quoi que ce soit avec l'interface utilisateur. J'ai différentes interfaces utilisateur «thématiques» qui implémentent iFoo et ont le même code de plaque de chaudière dans leur constructeur. Le needsAnIfoo a diverses propriétés qui sont databound à l'interface utilisateur (donc c'est aussi un modèle aussi.)

Il compile et fonctionne bien, mais je me demande s'il y a une meilleure façon.

Alors, ça sent?

interface ifoo 
    { 
     void bar(); 
    } 

    class foo : ifoo 
    { 
     readonly needsAnIfoo _needsAnIfoo; 
     internal foo() 
     { 
      _needsAnIfoo = new needsAnIfoo(this); 
     } 

     #region ifoo Members 
     public void bar() 
     { 
      throw new NotImplementedException(); 
     } 
     #endregion 
    } 

    class needsAnIfoo 
    { 
     readonly ifoo _myfoo; 
     public needsAnIfoo(ifoo foo) 
     { 
      _myfoo = foo; 
     } 
    } 

    static void Main(string[] args) 
    { 
     foo foo = new foo(); 
    } 

Peut-être que je devrais le needsAnIfoo new sans passer le IFoo dans le constructeur et lui donner la iFoo dans une méthode Initialize. Mais cela semble très étrange:

foo foo = new foo(); 
    needsAnIfoo needsAnIfoo = new needsAnIfoo(foo); 
    foo.Initialise(needsAnIfoo); 

Répondre

1

Cela ressemble à un endroit idéal pour instituer un modèle, je dirais la méthode d'usine.

class Factory 
{ 
public: 
    virtual needsAnIfoo* Create(ProductId); 
}; 

needsAnIfoo* Factory::Create(ProductId id) 
{ 
    if (id == TYPE1) return new needsAnIfoo(ifooType1()); 
    if (id == TYPE2) return new needsAnIfoo(ifooType2()); 
    ... 
    return 0; 
} 

Ensuite, vous l'utiliser comme ceci:

Factory f = new Factory(); 
theme1 = f.Create(TYPE1); 
theme2 = f.Create(TYPE2); 

modèles sont vos amis!

+0

Merci pour l'exemple! J'ai posté où je dois dans un instant. –

+0

Est-ce qu'un frère peut obtenir un upvote? :) Je commenterai directement sur vos affaires dans un instant ... – Josh

+0

Enfin vous a valu la note! (Je suis en train de passer en revue d'anciens messages et de fusionner des comptes, donc je viens juste de me souvenir de ce post.) –

1

Cela ne me semble pas correct. Odeurs fragiles.

Avez-vous envisagé d'utiliser un modèle constructeur ou un modèle d'usine pour créer les objets pertinents et établir les relations entre eux? Cela pourrait constituer un moyen plus sûr d'aller de l'avant.

+0

Oui, merci une usine semble être la voie à suivre. J'ai mis en place une usine prototype qui fabrique mes "iFoos" - je suis sur le point de poster le code. –

1

Je suis d'accord qu'un modèle Builder ou Factory, ou similaire, serait meilleur. Le code fourni n'est pas très testable et est, comme mentionné, plutôt fragile, donc une forme d'injection de dépendance serait bonne.

Le modèle à utiliser dépend de la façon dont foo et needsAnIFoo s'utilisent mutuellement. Vous devrez peut-être également prendre en compte le modèle Observer, si needsAnIFoo est un sujet, foo est un observateur et bar() est une méthode de mise à jour.

+0

Ok, j'ai réarrangé les choses légèrement - J'ai fait une Usine et j'ai utilisé un Evénement comme base pour un modèle d'observateur. Je vais poster ce que j'ai fait dans un moment. –

1

Il semble que cela peut être trop compliqué, et que vous faites votre thème un contrôleur et un contrôleur (en ayant les deux classes implémentent IFoo)

Vous pouvez obtenir de meilleurs résultats si vous séparez les concepts de thème et contrôleur, de sorte que le contrôleur a un thème. Ensuite, par exemple, quand le contrôleur fait quelque chose, comme ouvrir une boîte de dialogue, il regarde dans son thème pour trouver quelle police utiliser.

Comme ceci:

interface itheme {} // to describe properties of the theme 
class theme : itheme {}// a bunch of different themes, this previously would have been the "foo" 
class theme2 :itheme{} //etc. 

abstract class icontroller 
{ 
    protected icontroller(itheme ptheme) {theme = ptheme;} 

    protected itheme theme; 

    //function declarations 
    // .... 

} 
class control : icontroller {} // implements the icontrol functions. 
//not sure if you need more than one control implementation... 
// if not, i'd get rid of the icontrol interface. 


//use it by passing a theme into the controller constructor: 
icontroller myUIController = new control(new ClassicTheme()); 
+0

Ne pas harponner sur un point, mais si c'est le (nouveau?) Design, il faut absolument utiliser un motif de construction (méthode d'usine semble avoir le plus de sens). – Josh

+0

Ouais une méthode d'usine complèterait le design :) – dan

0

J'essaie de comprendre la question un peu, mais si chaque needsAnIfoo est liée à un seul type de classe et le needsAnIfoo lui-même ne fait rien pour semble que vous pouvez faire classe statique de needsAnIfoo avec les méthodes d'extension pas besoin de passer cela comme le constructeur arg.

extension method programming guide

0

est ici l'usine avec un observateur - semble faire l'affaire et évite newing le « contrôleur » à l'intérieur de mon interface utilisateur sur le thème.

interface ifoo 
    { 
     void bar(); 
    } 

    class foo : ifoo 
    { 
     public void bar() { Console.Write("do a foo type thing"); } 
    } 

    class foo2 : ifoo 
    { 
     public void bar() { Console.Write("do a foo2 type thing"); } 
    } 

    class needsAnIfoo 
    { 
     public event EventHandler SomethingIFooCanDealWith; 
     System.Threading.Timer _timer; 
     public needsAnIfoo() 
     { 
      _timer = new System.Threading.Timer(MakeFooDoSomething, null, 0, 1000); 
     } 

     void MakeFooDoSomething(Object state) 
     { 
      if (SomethingIFooCanDealWith != null) 
      { 
       SomethingIFooCanDealWith(this,EventArgs.Empty); 
      }; 
     } 
    } 

    class fooFactory 
    { 
     needsAnIfoo _needsAnIfoo = new needsAnIfoo(); 
     Dictionary<String, ifoo> _themedFoos = new Dictionary<string,ifoo>(); 
     ifoo _lastFoo = null; 

     public void RegisterFoo(String themeName, ifoo foo) 
     { 
      _themedFoos.Add(themeName, foo); 
     } 

     public ifoo GetThemedFoo(String theme) 
     { 
      if (_lastFoo != null) { _needsAnIfoo.SomethingIFooCanDealWith -= (sender, e) => _lastFoo.bar(); }; 
      ifoo newFoo = _themedFoos[theme]; 
      _needsAnIfoo.SomethingIFooCanDealWith += (sender, e) => newFoo.bar(); 
      _lastFoo = newFoo; 
      return newFoo; 
     } 
    } 

    static void Main(string[] args) 
    { 
     fooFactory factory = new fooFactory(); 
     factory.RegisterFoo("CompanyA", new foo()); 
     factory.RegisterFoo("CompanyB", new foo2()); 

     ifoo foo = factory.GetThemedFoo("CompanyA"); 
     Console.Write("Press key to switch theme"); 
     Console.ReadKey(); 

     foo = factory.GetThemedFoo("CompanyB"); 
     Console.ReadKey(); 
    } 
+1

Ce n'est pas vraiment une usine - c'est plus proche de la 'Flyweight Factory' proposée par Gamma et. Al. (p198). Un modèle de fabrique de manuels est utilisé pour construire de nouveaux objets; ne pas fournir de références à des objets existants. Cela étant dit, les motifs sont des lignes directrices. Je crois que cet extrait est très amélioré par rapport à l'original - et c'est un modèle applicable, par exemple, à la commutation en temps réel entre les thèmes (plutôt que sélectionné au démarrage, auquel une usine de manuels serait plus adaptée). – Josh

+0

Désolé - ceci est une usine; juste pas une usine de manuels scolaires. Pas du tout mauvais; juste clarifier l'intention du modèle d'usine! :RÉ – Josh