2009-07-28 3 views
12

Ich möchte eine Schnittstelle zurückgeben und in einer switch-Anweisung möchte ich es setzen. Ist das ein schlechtes Design?Ist eine Switch-Anweisung in einer Factory-Methode anwendbar? C#

private IResultEntity GetEntity(char? someType) 
    { 
     IResultEntity entity = null; 

     switch (someType) 
     { 
      case 'L': //life 
       entity = new LifeEntity(); 
       break; 
      case 'P': //property 
       entity = new PropertyEntity(); 
       break; 
      case 'D': //disability 
       entity = new DisabilityEntity(); 
       break; 
      case 'C': //credit card 
       entity = new CreditCardEntity(); 
       break; 
     } 

     return entity; 
    } 

Antwort

19

Normalerweise mache ich mir keine Gedanken über Schalteranweisungen in einer Fabrik, vorausgesetzt, ich kann alle abgeleiteten Klassen, die meine Fabrik im Voraus erstellen soll, gruppieren und steuern.

Manchmal möchte ein vom Benutzer erstelltes Plug-in möglicherweise eigene Klassen zu dieser Switch-Liste hinzufügen, und dann reicht eine swich-Anweisung nicht aus.

fand ich dieses gute source for some more info auf einige mächtiger/vielseitig Factory-Klassen Ich nehme in der Regel

Ein gutes Mittelgrund Ansatz schaffen, ist eine statische Wörterbuch < Zeichenfolge, Typ> für jede Fabrik Klasse zu halten.

Menschen können nur „registrieren“ ihre eigenen Implementierungen irgendeine Art von

Factories.TypeRegistration.StaticDictionary.Add("somekey",typeof(MyDerivedClass)) 

(oder besser noch, eine Registrierungsmethode verwenden und die StaticDictionary verstecken)

dann hat die Fabrik eine einfache Aufgabe, eine Instanz schaffen, indem eine Suche in der Tabelle durchführen:

Activator.CreateInstance(Factories.TypeRegistration.StaticDictionary["somekey"]); 
+0

So nähere ich mich normalerweise einer Factory, insbesondere in Bibliotheken, in denen Client-Apps möglicherweise eigene Implementierungen hinzufügen möchten. – Joon

+0

Diese Quelle für weitere Informationen Link ist nicht mehr verfügbar, jede Chance, dass es woanders gehostet wird? Oder gibt es irgendwo einen gleichwertigen Beitrag? –

+0

@Sam Heuck Ich verlinkte eine archivierte Version – Lennart

1

Ich würde nicht sagen, es ist ein schlechtes Design, obwohl es potenziell ziemlich starr ist. Die einzige Möglichkeit, dies zu erweitern, wäre die Neukompilierung.

3

Ich weiß nicht, welche Möglichkeiten Sie in C# haben, aber es ist immer noch besser, einen Schalter in einer Fabrikmethode zu haben, als Schalter überall zu haben. In einer Fabrikmethode ist ein Schalter tolerierbar - aber besser ist es gut dokumentiert.

+5

Diese Antwort fasst ziemlich genau zusammen, was ich sagen wollte. Ich würde hinzufügen, dass das einzige, was ich in Ihrer Factory-Implementierung ändern würde, wäre, dass ich eine Enumeration anstelle eines Nullable Char als Factory-Schlüssel verwenden würde. – wtaniguchi

+0

Wie konvertiere ich ein NULL-Zeichen in ein Enum? Ich dachte nicht, du könntest Streicher oder Zeichen singen. Ich dachte, sie repräsentieren numerische Werte. – Hcabnettek

+1

Definitiv stimme mit dem Enum über den "magischen" Charakter überein. Möglicherweise möchten Sie auch eine Ausnahme auslösen, wenn Sie die richtige Groß-/Kleinschreibung nicht finden können, anstatt Null zurückzugeben. Ich denke das könnte nur eine Vorliebe sein. – kevindaub

2

Ich glaube nicht, dass da etwas falsch ist. Ja, Schalteranweisungen sind ein Code-Geruch, aber in meinem Buch sind sie in einer solchen Situation in Ordnung. Es gibt eigentlich kaum etwas, was Sie tun könnten, um so etwas zu erreichen.

3

ich würde eher die Art, die Sie für einen bestimmten Wert in einer Konfigurationsdatei instanziiert werden sollen. Etwas wie:

<TypeMappings>
< TypeMapping name = "Leben" type = "Entities.LifeEntity, Entities"/>
< TypeMapping name = "Eigenschaft" type = "Entities.PropertyEntity, Entities"/>
< TypeMapping name = "Behinderung" type = "Entities.DisabilityEntity, Entities"/>
< TypeMapping name = "Kreditkarte" type = "Entities.CreditCardEntity, Entities"/>
TypeMappings</>

In Ihrer Methode, die Sie dann alle Anmeldungen aus der Config-Datei extrahieren können, finden Sie das passende ein und Reflektion verwenden den Typ instanziiert, wenn die Registrierung nicht gefunden wird, werfen Sie eine Ausnahme.Hier

ist ein Beispielcode:

namespace Entities 
{ 

public interface IResultEntity 
{ 
} 

public class LifeEntity : IResultEntity 
{ 
    public override string ToString() 
    { 
     return("I'm a Life entity"); 
    } 
} 

public class PropertyEntity : IResultEntity 
{ 
    public override string ToString() 
    { 
     return("I'm a Property Entity"); 
    } 
} 

public class CreditCardEntity : IResultEntity 
{ 
    public override string ToString() 
    { 
     return("I'm a CreditCard Entity "); 
    } 
} 

public class DisabilityEntity : IResultEntity 
{ 
    public override string ToString() 
    { 
     return("I'm a Disability Entity"); 
    } 
} 

}

public static Entities.IResultEntity GetEntity(string entityTypeName,string fileName) 
{ 
    XDocument doc = XDocument.Load(fileName); 
    XElement element = doc.Element("TypeMappings").Elements("TypeMapping") 
           .SingleOrDefault(x => x.Attribute("name").Value == entityTypeName);   

    if(element == null) 
    { 
     throw new InvalidOperationException("No type mapping found for " + entityTypeName); 
    } 
    string typeName = element.Attribute("type").Value; 
    Type type = Type.GetType(typeName); 
    Entities.IResultEntity resultEntity = Activator.CreateInstance(type) as Entities.IResultEntity; 
    if(resultEntity == null) 
    { 
     throw new InvalidOperationException("type mapping for " + entityTypeName + " is invalid"); 
    } 
    return resultEntity; 
} 

    public static void Main() 
{ 
    try 
    { 
     Entities.IResultEntity result = GetEntity("life", @"c:\temp\entities.xml"); 
     Console.WriteLine(result); 

     result = GetEntity("property", @"c:\temp\entities.xml"); 
     Console.WriteLine(result); 

     result = GetEntity("disability", @"c:\temp\entities.xml"); 
     Console.WriteLine(result);   

     result = GetEntity("creditcard", @"c:\temp\entities.xml"); 
     Console.WriteLine(result);   

     result = GetEntity("foo", @"c:\temp\entities.xml"); 
     Console.WriteLine(result);  

    } 
} 

Viele DI-Frameworks können Sie mehrere Registrierungen für eine Schnittstelle zur Verfügung stellen, die Sie auf Metadaten Abfrage basiert können. Schauen Sie sich this link an, wie MEF exportiert mit Metadaten.

+0

sieht aus wie Overkill –

2

Es ist nicht schlecht, es ist fast genau das selbe wie ein Beispiel (Parametrisierte Fabrik Methode) in der Viererbande selbst.

Ich dachte, dass Switch-Anweisungen ein Code-Geruch sind, sie sind nicht, sie haben ihren Platz in jeder OO-Sprache.

Verwandte Themen