2011-08-26 10 views
8

Secondo i principi SOLID una classe non può dipendere da altre classi, le dipendenze devono essere iniettate. E 'semplice:Inversione di dipendenza. Creazione dell'oggetto

class Foo 
{ 
    public Foo(IBar bar) 
    { 
     this.bar = bar; 
    } 

    private IBar bar; 
} 

interface IBar 
{ 
} 

class Bar: IBar 
{ 
} 

Ma cosa succede se voglio la mia classe Foo di essere in grado di creare bar di, non conoscendo l'esatta esecuzione dietro IBar? mi viene in mente 4 soluzioni qui, ma tutti sembrano avere svantaggi:

  1. che iniettano il tipo di oggetto e l'utilizzo di riflessione
  2. utilizzando Generics
  3. utilizzando "Service Locator" e chiamando il Resolve () metodo.
  4. creando una classe di fabbrica separato e l'immissione nella Foo:

class Foo 
{ 
    public void DoSmth(IBarCreator barCreator) 
    { 
     var newBar = barCreator.CreateBar(); 
    } 
} 

interface IBarCreator 
{ 
    IBar CreateBar(); 
} 

class BarCreator : IBarCreator 
{ 
    public IBar CreateBar() 
    { 
     return new Bar(); 
    } 
} 

ultimo caso sembra naturale, ma di classe BarCreator ha il codice troppo litle. Quindi, come pensi, qual è il migliore?

+1

L'opzione 4 è la risposta corretta: http://stackoverflow.com/questions/1943576/is-there-a-pattern-for-initializing-objects-created-via-a-di-taintainer/1945023#1945023 –

+1

Tuttavia, perché vuoi che Foo crei l'IBar? Sii consapevole delle Abstraction Leaky. –

risposta

0

Mi sento quando dici "Voglio che la mia classe Foo sia in grado di creare Bar", un'altra regola viene a giocare che è "Separazione delle preoccupazioni". Quindi dovresti delegare il compito della creazione di classe a qualcos'altro e Foo non dovrebbe essere preoccupato per quel compito.

2

tutto dipende dallo scenario esatto e dalle vostre esigenze.
Penso che l'approccio più utilizzato sia, come hai detto, uno factory.
Se si utilizza un framework IoC (come Ninject, o Sprint.Net, Castle Windsor ecc. Vedere here), anche un localizzatore di servizi è una soluzione valida.

3

Mi piace "iniettare" un Func<IBar> in questo caso. Mi piace così:

class Foo 
{ 
    public Foo(Func<IBar> barCreator) 
    { 
     this.bar = barCreator(); 
    } 

    private IBar bar; 
} 

interface IBar 
{ 
} 
2

Se si ha un problema con l'interfaccia di fabbrica ridondante, si possono fare due approcci qui.

renderlo riutilizzabile con i generici:

interface IFactory<T> 
{ 
T Create(); 
} 

class DefaultConstructorFactory<T> : IFactory<T>, where T: new() 
{ 
public T Create() { return new T();} 
} 

Oppure utilizzare la funzione anonima come una fabbrica:

public void DoSomething(Func<IBar> barCreator) 
{ 
var newBar = barCreator(); 
//... 
} 
+0

Questo in realtà non funziona vero? Non è possibile passare un 'DefaultConstructorFactory 'come' IFactory ' – fearofawhackplanet

3

Questo è ciò che le fabbriche sono state fatte per.

Se ritenete che la vostra fabbrica abbia un codice troppo piccolo, chiedetevi quali vantaggi vi offre semplicemente creando l'istanza in linea. Se i benefici superano i costi del codice aggiunto, non preoccuparti.

Eviterei personalmente il luogo del servizio, o se proprio dovessi usarlo lo nasconderei comunque dietro una fabbrica. La posizione del servizio tende ad essere facilmente abusata e può portare il tuo contenitore a trovare la sua strada in codice che non dovrebbe avere nulla a che fare con.

Per comodità, alcuni contenitori consentono di specificare una fabbrica da utilizzare dal contenitore quando crea un'istanza di un componente. In questo caso, la tua classe potrebbe dipendere direttamente da IBar ma il tuo contenitore chiamerebbe IBarCreator quando ha bisogno di una nuova istanza.Castle Windsor ha i metodi UseFactory e UseFactoryMethod nella loro API, ad esempio.