2013-10-08 14 views
5

Diciamo che voglio creare un elenco di stringhe (che non è il caso dello scenario reale, ma suona più semplice da spiegare).Se una fabbrica ha un costruttore con parametri?

avrei un'interfaccia per la mia lista di stringhe fabbrica che sarebbe simile a questa

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

Ma diciamo un mio fabbrica di cemento richiederebbe per ottenere questo elenco di stringa da un file/etc di database ..

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... 
    } 
} 

so che questo approccio avrebbe funzionato, ma mi chiedevo se si rompe il modello di fabbrica per passare i parametri al costruttore della fabbrica in modo da non rompere il mio interfaccia. Ci sono delle controparti a fare questo? C'è un'altra soluzione a cui non ho pensato? Sto facendo troppe domande!?! (Sì, conosco la risposta a questo!)

risposta

3

Il parametro nel costruttore e il costruttore stesso devono eseguire solo uno e un solo lavoro: la registrazione della dipendenza. A volte, è necessario "iniettare" la dipendenza dalla fabbrica, come descritto nel modello di fabbrica astratto di Mark Seeman in this answer.

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... 
    } 
} 

È valido in questo caso, ma non nel tuo caso perché:

  1. Rende la fabbrica ha stato
  2. Rende la fabbrica più flessibile se il parametro (stream) iniettato come parametro del metodo

    public class StringsListFromFile : IStringsListFactory{ 
    
        public List<string> Create(StreamReader sr){ 
        ///recover the strings using my stream reader... 
        } 
    } 
    
  3. Se l'interfaccia dovrebbe essere flessibile per l'ingresso, uso generico invece

  4. Inoltre, è meglio tornare IEnumerable<string> invece di List<string>
+0

Perché la mia fabbrica ha uno stato da quando il mio membro è incluso nella mia classe? E sta restituendo IEnumerable un problema di prestazioni? – TopinFrassi

+0

Usando il mio disegno al punto 2, puoi istanziare il tuo corso una volta, e poi usare diversi 'StreamReader' senza dover creare un altro oggetto della stessa classe. È un'altra storia, se il tuo StreamReader iniettato è una specie di configurazione globale. Ma ha bisogno di un altro design per ottenere risultati migliori. – Fendy

+0

Restituire 'IEnumerable' è solo un'abitudine di fare' Programma su un'interfaccia piuttosto che su un programma per l'implementazione'. L'interfaccia che restituisce "IEnumerable" può far sì che restituisca anche un array, o un'altra raccolta come "ObersvableCollection". – Fendy

0

Si potrebbe astrarre l'implementazione di qualsiasi cosa lo recuperi. Vorrei anche personalmente passare attraverso al metodo al posto del costruttore:

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

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

Allora forse:

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

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

Tutto questo sembra sciocco per un piccolo campione di tale .. ma suppongo questo non è abbastanza piccolo come il tuo esempio.

+0

In questa situazione, dove sarebbe lo StreamReader ottenere gli argomenti di suo costruttore? Penso davvero che DataSourceProvider sia una buona idea, ma non sono sicuro di capirlo appieno in questo caso, forse perché il mio esempio è davvero troppo piccolo. – TopinFrassi

+0

Dipende interamente dalla configurazione corrente. Senza saperne di più è difficile da dire.Generalmente lo si passerebbe attraverso il costruttore anche se ... dove lo si ottiene in anticipo, ma dipende dall'implementazione del codice più in alto. –

0

Modello di fabbrica obbliga a utilizzare Costruttore predefinito. L'utilizzo di un costruttore parametrico viola l'idea di utilizzare il modello di fabbrica poiché l'oggetto non restituirà lo stato valido alla classe del chiamante. Nel tuo caso, devi inizializzarli dopo la chiamata della classe factory. Questo duplicherà il tuo codice e l'idea di usare lo schema di fabbrica è di evitare la duplicazione del codice. Ma ancora non ho familiarità con l'intero scenario. Tuttavia, come mostrato qui, dovresti usare un metodo al posto di Parametric Constructor.

Problemi correlati