Come usare l'iniezione delle dipendenze insieme al modello Factory


10

Prendi in considerazione un modulo responsabile dell'analisi dei file di qualsiasi tipo. Sto pensando di utilizzare il modello strategico per affrontare questo problema, come ho già spiegato qui . Si prega di fare riferimento al post collegato prima di procedere con questa domanda.

Considera la classe B che necessita del contenuto del file product.xml. Questa classe dovrà istanziare l'implementatore concreto appropriato dell'interfaccia Parser per analizzare il file XML. Posso delegare l'istanza dell'attuatore concreto appropriato a una Fabbrica in modo tale che la Classe B "abbia-una" Fabbrica. Tuttavia, la classe B "dipenderà" da una fabbrica per creare un'istanza dell'attuatore concreto. Ciò significa che il costruttore o un metodo setter in classe B dovranno essere passati alla Factory.

Pertanto, la Factory e la classe B che devono analizzare un file saranno strettamente accoppiati tra loro. Capisco che potrei sbagliarmi completamente su ciò che ho spiegato finora. Vorrei sapere se posso usare l'iniezione di dipendenza in uno scenario in cui la dipendenza da iniettare è una Fabbrica e quale sarebbe il modo giusto per implementarla in modo da poter sfruttare aree come deridere la Fabbrica nei test delle mie unità.

Risposte:


8

Il modo giusto per farlo è dipendere da un'interfaccia e quindi iniettare un'implementazione di tale interfaccia nella classe B.

Un'interfaccia è quasi la cosa più sottile su cui puoi contare - la paragonerei a cercare di afferrare un filo di fumo. Il codice deve accoppiarsi a qualcosa altrimenti non farà nulla, ma l'accoppiamento a un'interfaccia è il più possibile disaccoppiato, ma le interfacce possono fornire tutte le funzionalità che potresti desiderare.

Quindi chiedi al costruttore di Classe B di prendere un'interfaccia per la classe di cui ha bisogno e chiedi alla fabbrica di produrre quella classe come implementatore dell'interfaccia. Non dipendere dalla fabbrica, dipendere dall'interfaccia e far sì che la fabbrica fornisca un'implementazione di quella fabbrica.

Quindi sì, userete l'iniezione di dipendenza, ma non c'è niente di sbagliato in questo. L'iniezione di dipendenza - in particolare l'iniezione del costruttore semplice - dovrebbe essere il modo "normale" di fare le cose. Basta rimandare le cose nuove il più indietro possibile nella tua app (e il più vicino possibile alla prima riga di main) e nascondere la nuova chiamata in una classe progettata appositamente per creare cose.

In conclusione: non esitare a iniettare dipendenze. Questo dovrebbe essere il modo normale di fare le cose.


L'iniezione del costruttore rimanda le cose nuove il più a lungo possibile?
JeffO,

Era sbagliato - l'ho corretto per dire "il più indietro possibile nella tua app".
Nick Hodges,

Sì. Stavo pensando di dipendere dall'interfaccia Parser invece che da una fabbrica. Ora, se un'altra classe utilizza la classe B, dovrà dipendere dall'interfaccia da cui dipende la classe B. Ciò continuerà fino alla classe di massimo livello. Questa classe di livello superiore dovrà finalmente utilizzare una fabbrica per creare un'istanza concreta appropriata del parser. La classe di livello superiore dovrebbe dipendere da una fabbrica o dovrebbe usare la fabbrica direttamente all'interno dei suoi metodi?
CKing

1
Ben detto: non esitare a iniettare dipendenze
Signcodeindie il

9

Penso che la tua premessa sia un po 'confusa qui, tu parli di iniettare una fabbrica, ma il modello di fabbrica è un modello di creazione il cui scopo era fare un sottoinsieme di ciò che fa un framework di iniezione di dipendenza, quando i framework DI non erano prevalenti questo schema era utile per quel motivo. Tuttavia, se si dispone di un framework DI, non è più realmente necessario un factory poiché il framework DI può soddisfare lo scopo che la factory avrebbe realizzato.

Detto questo, lasciatemi spiegare un po 'sull'iniezione di dipendenza e su come la usereste generalmente.

Esistono diversi modi per eseguire l'iniezione di dipendenza, ma i più comuni sono l'iniezione del costruttore, l'iniezione di proprietà e il DIContainer diretto. Parlerò dell'iniezione del costruttore poiché l'iniezione di proprietà è l'approccio sbagliato per la maggior parte del tempo (l'approccio giusto qualche volta) e l'accesso a DIContainer non è preferibile, tranne quando non è assolutamente possibile eseguire nessuno degli altri approcci.

L'iniezione del costruttore è dove hai l'interfaccia per una dipendenza e un DIContainer (o fabbrica) che conosce l'implementazione concreta per quella dipendenza, e ovunque tu abbia bisogno di un oggetto che dipende da quell'interfaccia, al momento della costruzione devi consegnare l'implementazione dalla fabbrica a esso.

vale a dire

IDbConnectionProvider connProvider = DIContainer.Get<IDbConnectionProvider>();
IUserRepository userRepo = new UserRepository(connProvider);
User currentUser = userRepo.GetCurrentUser();

Molti framework DI possono semplificare in modo significativo il punto in cui il DIContainer ispezionerà il costruttore di UserRepository per le interfacce per le quali conosce implementazioni concrete e le passerà automaticamente per te; questa tecnica è spesso chiamata Inversion of Control, sebbene DI e IoC siano entrambi termini che vengono scambiati molto e hanno vaghe (se presenti) differenze.

Ora, se ti stai chiedendo come il codice globale acceda al DIContainer, puoi anche avere una classe statica per accedervi o ciò che è più appropriato è che la maggior parte dei framework DI ti consente di rinnovare un DIContainer, in cui si comporterà come un wrapper per un dizionario singleton interno per i tipi che sa essere concreti per determinate interfacce.

Ciò significa che è possibile rinnovare DIContainer ovunque nel codice e ottenere effettivamente lo stesso DIContainer che era già stato configurato per conoscere le relazioni tra interfaccia e calcestruzzo. I mezzi usuali per nascondere il DIContainer da parti del codice che non dovrebbero interagire direttamente con esso è semplicemente garantire che solo i progetti necessari abbiano un riferimento al framework DI.


Non sono pienamente d'accordo con il primo paragrafo. Esistono ancora scenari in cui si dipende da una factory (implementando inteface) che viene iniettata dal contenitore DI.
Piotr Perak,

Penso che ti sia sfuggito il punto poiché l'OP non parlava di quadri DI o contenitori DI.
Doc Brown,

@Jimmy Hoffa Mentre hai fatto alcuni punti molto interessanti, sono a conoscenza di contenitori IoC come Spring e del concetto di Dependency Injection. La mia preoccupazione riguardava uno scenario in cui non si utilizza un framework IoC, nel qual caso è necessario scrivere una classe che creerà istanze per le dipendenze. Se la classe A dipende dall'interfaccia B e la classe C utilizza la classe A, allora la classe C dipende dall'interfaccia B. Qualcuno deve assegnare alla classe C una classe B. La classe C dovrebbe quindi dipendere da cla
CKing del

Se la classe A dipende dall'interfaccia B e la classe C utilizza la classe A, allora la classe C dipende dall'interfaccia B. Qualcuno deve fornire alla classe C un'interfaccia B. La classe C dovrebbe quindi dipendere dall'interfaccia B o dovrebbe dipendere dalla classe che ha istanziato il dipendenze Vale a dire la fabbrica. Sembra che tu abbia risposto a questa domanda nella tua risposta, ma con un sovraccarico di informazioni :)
CKing

@bot come ho detto nella prefazione, in gran parte puoi trattare le fabbriche come un sottoinsieme della funzionalità di un contenitore DI, non è vero in tutti gli scenari come ha menzionato Doc Brown, ma guarda il mio esempio di codice e sostituisci DIContainercon DbConnectionFactorye il concetto resta comunque che recupererai l'implementazione concreta dal tuo DI / Factory / etc e la consegneresti ai consumatori del tipo al momento della loro costruzione.
Jimmy Hoffa,

5

Puoi passare una fabbrica tramite l'iniezione delle dipendenze proprio come fai per qualsiasi altra cosa, non lasciare che la ricorsività della situazione ti confonda. Non so cos'altro dire sull'implementazione: sai già come fare l'iniezione di dipendenza.

Uso DI per iniettare fabbriche abbastanza regolarmente.


1
Se una classe dipende da una Fabbrica, dovrai deridere la Fabbrica quando l'unità verifica quella classe. Come fai a farlo? Che la classe dipenda da un'interfaccia di fabbrica?
CKing

4

Non c'è niente di sbagliato nell'iniettare fabbriche. È un modo standard se non è possibile decidere durante la compilazione dell'oggetto 'genitore' quale tipo di dipendenza sarà necessaria. Penso che l'esempio lo spiegherà meglio. Userò c # perché non conosco java.


class Parent
{
    private IParserFactory parserFactory;
    public Parent(IParserFactory factory)
    {
        parserFactory = factory
    }

    public ParsedObject ParseFrom(string filename, FileType fileType)
    {
        var parser = parserFactory.CreateFor(fileType);
        return parser.Parse(filename);
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.