2013-10-08 7 views
5

Sagen wir, ich möchte eine Liste von Strings erstellen (was nicht der Fall des realen Szenarios ist, aber klingt einfacher zu erklären).Sollte eine Fabrik einen Konstruktor mit Parametern haben?

ich eine Schnittstelle für meine Liste von Strings Fabrik haben würde, die wie diese

public interface IStringsListFactory{ 
    List<string> Create(); 
} 

aussehen würden aber läßt eine meiner konkreten Fabrik sagen würde erfordern, diese Liste des Zeichenfolge aus einer Datei/Datenbank erhalten usw. ..

public class StringsListFromFile : IStringsListFactory{ 

    private StreamReader _streamReader; 
    public StringsListFromFile(StreamReader sr) //StreamReader is just an example. 
    { 
     _streamReader = sr; 
    } 

    public List<string> Create(){ 
    ///recover the strings using my stream reader... 
    } 
} 

ich weiß, dass dieser Ansatz funktionieren würde, aber ich frage mich, ob es das Werk Muster passieren Parameter auf das Werk Konstruktor bricht so würde ich nicht meine Schnittstelle brechen. Gibt es Gegenstücke dazu? Gibt es eine andere Lösung, an die ich nicht gedacht habe? Stelle ich zu viele Fragen!?! (Ja, ich kenne die Antwort auf dieses!)

Antwort

3

Parameter im Konstruktor und Konstruktor selbst sollten nur einen einzigen Job erledigen: das ist die Registrierung der Abhängigkeit. Manchmal ist es erforderlich, die Abhängigkeit in die Fabrik zu "injizieren", wie dies bei dem abstrakten Fabrikmuster von Mark Seeman in this answer beschrieben ist.

public class ProfileRepositoryFactory : IProfileRepositoryFactory 
{ 
    private readonly IProfileRepository aRepository; 
    private readonly IProfileRepository bRepository; 

    public ProfileRepositoryFactory(IProfileRepository aRepository, 
     IProfileRepository bRepository) 
    { 
     if(aRepository == null) 
     { 
      throw new ArgumentNullException("aRepository"); 
     } 
     if(bRepository == null) 
     { 
      throw new ArgumentNullException("bRepository"); 
     } 

     this.aRepository = aRepository; 
     this.bRepository = bRepository; 
    } 

    public IProfileRepository Create(string profileType) 
    { 
     if(profileType == "A") 
     { 
      return this.aRepository; 
     } 
     if(profileType == "B") 
     { 
      return this.bRepository; 
     } 

     // and so on... 
    } 
} 

Es ist in diesem Fall gültig, aber nicht in Ihrem Fall, weil:

  1. Es Ihre Fabrik
  2. Zustand macht
  3. haben Es macht Ihre Fabrik flexibler, wenn der Parameter (Strom) injiziert als Methodenparameter

    public class StringsListFromFile : IStringsListFactory{ 
    
        public List<string> Create(StreamReader sr){ 
        ///recover the strings using my stream reader... 
        } 
    } 
    
  4. Wenn Ihre Schnittstelle zur Eingabe flexibel sein sollte, verwenden Generika statt

  5. zusätzlich ist es besser IEnumerable<string> statt List<string>
+0

Warum macht meine Fabrik einen Status, da mein Mitglied in meiner Klasse eingeschlossen ist? Und gibt IEnumerable ein Leistungsbedenken zurück? – TopinFrassi

+0

Mit meinem Design in Punkt 2 können Sie Ihre Klasse einmal instanziieren und dann einen anderen 'StreamReader' verwenden, ohne ein anderes Objekt derselben Klasse erstellen zu müssen. Es ist eine andere Geschichte, wenn dein injizierter "StreamReader" eine Art globaler Konfiguration ist. Aber es braucht ein anderes Design für bessere Leistung. – Fendy

+0

Die Rückkehr von "IEnumerable" ist nur eine Angewohnheit, 'Programm zu einer Schnittstelle zu machen, statt Programm zur Implementierung '. Wenn die Schnittstelle "IEnumerable" zurückgibt, kann sie auch ein Array oder eine andere Sammlung wie "ObersvableCollection" zurückgeben. – Fendy

0

Sie könnten die Implementierung von was auch immer abruft weg abstrahieren. Ich persönlich würde es auch passieren in das Verfahren durch, anstatt der Konstruktor:

public interface IFactoryDataSourceProvider<T> { 
    IList<T> GetData(); 
} 

public class IStringListFactory { 
    public IList<string> Create(IFactoryDataSourceProvider<string> provider) { 
     return _provider.GetData(); 
    } 
} 

Dann vielleicht:

class StreamReaderDataProvider : IFactoryDataSourceProvider<string> { 
    public IList<string> GetData() { 
     using (var streamReader = new StreamReader(...)) { 
      return streamReader.ReadAllLines(); // etc. 
     } 
    } 
} 

var list = factory.Create(new StreamReaderDataSourceProvider()); 

Das alles für so eine kleine Probe albern scheint .. aber ich nehme an, dies ist nicht so klein wie dein Beispiel ist.

+0

In dieser Situation zurückzukehren, wo würde der Stream seinen Konstruktor Argumente bekommen? Ich denke wirklich, dass der DataSourceProvider eine gute Idee ist, aber ich bin mir nicht sicher, ob ich ihn in diesem Fall vollständig verstehe, vielleicht weil mein Beispiel tatsächlich zu klein ist. – TopinFrassi

+0

Das hängt vollständig von Ihrem aktuellen Setup ab. Ohne es zu wissen, ist es schwer zu sagen.Im Allgemeinen würden Sie es durch den Konstruktor obwohl .. übergeben. Wo Sie es von im Voraus erhalten, ist bis zur Implementierung des Codes höher. –

0

Factory-Muster zwingt Sie, den Standardkonstruktor zu verwenden. Die Verwendung eines parametrischen Konstruktors verstößt gegen die Idee, ein Factory-Muster zu verwenden, da das Objekt den gültigen Status nicht an die Klasse des Aufrufers zurückgibt. In Ihrem Fall müssen Sie sie nach dem Aufruf der Factory-Klasse initialisieren. Dadurch wird Ihr Code dupliziert und die Idee, das Factory-Muster zu verwenden, besteht darin, die Code-Duplizierung zu vermeiden. Aber ich bin wieder nicht vertraut mit dem ganzen Szenario. Aber gemäß Ihrer hier gezeigten Konfiguration sollten Sie eine Methode anstelle von Parametric Constructor verwenden.

Verwandte Themen