dependency injection - ioc - Iniezione delle dipendenze rispetto al modello di fabbrica




ioc container java (18)

La gestione del ciclo di vita è una delle responsabilità che i contenitori di dipendenza assumono in aggiunta all'istanziazione e all'iniezione. Il fatto che a volte il contenitore mantenga un riferimento ai componenti dopo l'istanziazione è il motivo per cui viene chiamato un "contenitore" e non una fabbrica. I contenitori per l'iniezione delle dipendenze di solito mantengono solo un riferimento agli oggetti di cui ha bisogno per gestire i cicli di vita, o che vengono riutilizzati per iniezioni future, come singleton o corpi volanti. Quando configurato per creare nuove istanze di alcuni componenti per ogni chiamata al contenitore, il contenitore di solito si dimentica solo dell'oggetto creato.

Da: http://tutorials.jenkov.com/dependency-injection/dependency-injection-containers.html

La maggior parte degli esempi citati per l'uso di Dependency Injection, possiamo risolvere usando anche il modello factory. Sembra che quando si tratta di usare / progettare la differenza tra l'iniezione di dipendenza e la fabbrica sia sfocata o sottile.

Una volta qualcuno mi ha detto che il modo in cui lo usi fa la differenza!

Una volta ho usato StructureMap come contenitore DI per risolvere un problema, in seguito l'ho ridisegnato per funzionare con un semplice factory e rimosso i riferimenti a StructureMap.

Qualcuno può dirmi qual è la differenza tra loro e dove usare cosa, qual è la migliore pratica qui?


Teoria

Ci sono due punti importanti da considerare:

  1. Chi crea oggetti

    • [Fabbrica]: Devi scrivere COME deve essere creato l'oggetto. Hai una classe Factory separata che contiene la logica di creazione.
    • [Iniezione di dipendenza]: nei casi pratici vengono eseguite da framework esterni (ad esempio in Java che sarebbe spring / ejb / guice). L'iniezione avviene "magicamente" senza la creazione esplicita di nuovi oggetti
  2. Che tipo di oggetti gestisce:

    • [Factory]: solitamente responsabile della creazione di oggetti stateful
    • [Iniezioni di dipendenza] Più probabilità di creare oggetti senza stato

Esempio pratico su come utilizzare sia l'iniezione di fabbrica che quella di dipendenza in un singolo progetto

  1. Cosa vogliamo costruire

Modulo di applicazione per la creazione di ordini che contiene più voci chiamate ordine.

  1. Architettura

Supponiamo di voler creare l'architettura seguente:

Gli oggetti dominio possono essere oggetti memorizzati nel database. Repository (DAO) aiuta con retrievar di oggetti dal database. Il servizio fornisce l'API ad altri moduli. Consente le operazioni sul modulo order

  1. Livello di dominio e utilizzo delle fabbriche

Le entità che saranno nel database sono Order e OrderLine. L'ordine può avere più ordini.

Ora arriva una parte importante del design. I moduli esterni a questo dovrebbero creare e gestire OrderLines da soli? No. La riga d'ordine deve esistere solo quando si ha un ordine associato. Sarebbe meglio se potessi nascondere l'implementazione interna a classi esterne.

Ma come creare un ordine senza conoscenza di OrderLines?

Fabbrica

Qualcuno che vuole creare un nuovo ordine ha usato OrderFactory (che nasconderà i dettagli sul fatto che creiamo Ordine).

Ecco come apparirà all'interno dell'IDE. Le classi al domain fuori del pacchetto di domain useranno OrderFactory invece del costruttore all'interno Order

  1. Iniezione delle dipendenze L'iniezione delle dipendenze è più comunemente utilizzata con livelli senza stato come repository e servizi.

OrderRepository e OrderService sono gestiti da framework di injection dependency. Il repository è responsabile della gestione delle operazioni CRUD sul database. Il servizio inserisce il repository e lo utilizza per salvare / trovare classi di dominio corrette.


Ci sono problemi che sono facili da risolvere con l'iniezione di dipendenza che non sono facilmente risolvibili con una serie di fabbriche.

Una parte della differenza tra, da un lato, l'inversione dell'iniezione di controllo e dipendenza (IOC / DI) e, dall'altro, un localizzatore di servizi o una serie di fabbriche (fabbrica), è:

IOC / DI è un ecosistema completo di oggetti e servizi di dominio in sé e per sé. Imposta tutto per te nel modo specificato. I tuoi oggetti e servizi di dominio sono costruiti dal contenitore e non si costruiscono da soli: pertanto non hanno alcuna dipendenza dal contenitore o da alcuna fabbrica. IOC / DI consente un altissimo grado di configurabilità, con tutta la configurazione in un unico punto (costruzione del contenitore) al livello più alto dell'applicazione (la GUI, il front-end Web).

Factory astrae parte della costruzione dei tuoi oggetti e servizi di dominio. Ma gli oggetti ei servizi di dominio sono ancora responsabili di capire come costruire se stessi e come ottenere tutte le cose da cui dipendono. Tutte queste dipendenze "attive" filtrano attraverso tutti i livelli dell'applicazione. Non c'è un unico posto dove andare per configurare tutto.


Con l'iniezione di dipendenza il client non ha bisogno di ottenere le sue dipendenze da solo, è tutto pronto in anticipo.

Con le fabbriche, qualcuno deve chiamare quelle per portare gli oggetti generati nel luogo in cui sono necessari.

La differenza risiede principalmente in questa linea in cui si chiama la fabbrica e si recupera l'oggetto costruito.

Ma con le fabbriche devi scrivere questa 1 linea ovunque hai bisogno di un tale oggetto. Con DI devi solo creare il cablaggio (relazione tra utilizzo e oggetto creato) una volta e contare sulla presenza dell'oggetto in seguito ovunque. Dall'altro lato, DI richiede spesso un po 'più (quanto dipende dal framework) il lavoro sul lato della preparazione.


Credo che 3 aspetti importanti governino gli oggetti e il loro utilizzo:
1. Istanziazione (di una classe insieme all'inizializzazione, se presente).
2. Iniezione (dell'istanza così creata) dove è richiesta.
3. Gestione del ciclo di vita (dell'istanza così creata).

Usando il modello di fabbrica, si ottiene il primo aspetto (istanziazione) ma i rimanenti due sono discutibili. La classe che utilizza altre istanze deve codificare le fabbriche (anziché creare istanze) che impediscono le abilità di accoppiamento lente. Inoltre, la gestione del ciclo di vita delle istanze diventa una sfida in una grande applicazione in cui una fabbrica viene utilizzata in più luoghi (in particolare, se la fabbrica non gestisce il ciclo di vita dell'istanza restituita, diventa brutta).

D'altra parte, usando una DI (di pattern IoC), tutti i 3 sono estratti fuori dal codice (al contenitore DI) e il bean gestito non ha bisogno di nulla su questa complessità. Loose Coupling , un obiettivo architettonico molto importante può essere raggiunto tranquillamente. Un altro importante traguardo architettonico, la separazione delle preoccupazioni può essere ottenuta molto meglio delle fabbriche.

Mentre le fabbriche possono essere adatte a piccole applicazioni, quelle di grandi dimensioni potrebbero essere preferibili a DI rispetto alle fabbriche.


Credo che DI sia un modo di configurare o istanziare un bean. Il DI può essere fatto in molti modi come costruttore, setter-getter ecc.

Il modello di fabbrica è solo un altro modo di creare un'istanza di fagioli. questo modello verrà utilizzato principalmente quando è necessario creare oggetti utilizzando il modello di progettazione di fabbrica, perché mentre si utilizza questo modello non si configura le proprietà di un bean, si crea solo un'istanza dell'oggetto.

Controlla questo link: Iniezione delle dipendenze


Ho avuto la stessa domanda non appena ho letto su DI e sono finito in questo post. Quindi finalmente questo è quello che ho capito ma per favore correggimi se sbaglio.

"Molto tempo fa c'erano piccoli regni con i loro stessi organi di governo che controllavano e prendevano decisioni in base alle loro stesse regole scritte.In seguito formò un grande governo che elimina tutti questi piccoli organismi governativi che hanno una serie di regole (costituzione) e sono attuate attraverso i tribunali"

I corpi governativi dei piccoli regni sono "Fabbriche"

Il grande governo è "l'iniettore di dipendenza".


I miei pensieri:

Iniezione della dipendenza: passare i collaboratori come parametri ai costruttori. Dipendenza dell'iniezione Framework: una fabbrica generica e configurabile per creare gli oggetti da passare come parametri ai costruttori.


La maggior parte delle risposte qui spiega le differenze concettuali e i dettagli di implementazione di entrambi. Tuttavia non sono stato in grado di trovare spiegazioni sulla differenza nell'applicazione che l'IMO è il più importante e l'OP ha chiesto. Quindi permettimi di riaprire questo argomento ...

Una volta qualcuno mi ha detto che il modo in cui lo usi fa la differenza!

Esattamente. Nel 90% dei casi è possibile ottenere riferimenti agli oggetti usando Factory o DI e di solito si finisce con quest'ultimo. In un altro 10% dei casi l'utilizzo di Factory è solo modo corretto . Questi casi includono l'ottenimento di oggetti in base alla variabile ai parametri di runtime. Come questo:

IWebClient client = factoryWithCache.GetWebClient(url: ".com",
        useCookies: false, connectionTimeout: 120);

In questo caso non è possibile ottenere client da DI (o almeno richiede una brutta soluzione). Quindi, come regola generale per prendere una decisione: se si può ottenere una dipendenza senza parametri di runtime calcolati, si preferisce DI, altrimenti usare Factory.


La ragione per cui Dipendenza Iniezione (DI) e Modelli di fabbrica sono simili è perché sono due implementazioni di Inversion of Control (IoC) che è un'architettura software. In parole povere sono due soluzioni allo stesso problema.

Quindi, per rispondere alla domanda, la differenza principale tra il modello di fabbrica e il DI è il modo in cui viene ottenuto il riferimento dell'oggetto. Con l'iniezione di dipendenza come il nome implica che il riferimento è iniettato o dato al tuo codice. Con il modello Factory il codice deve richiedere il riferimento in modo che il codice recuperi l'oggetto. Entrambe le implementazioni rimuovono o disaccoppiano il collegamento tra il codice e la classe o il tipo sottostante del riferimento oggetto utilizzato dal codice.

Vale la pena notare che i pattern di fabbrica (o in effetti i pattern di Factory astratti che sono fabbriche che restituiscono nuove fabbriche che restituiscono riferimenti a oggetti) possono essere scritti per scegliere dinamicamente o collegare al tipo o alla classe di oggetto richiesta in fase di esecuzione. Ciò li rende molto simili (anche più di DI) al modello di Localizzazione del servizio, che è un'altra implementazione dell'IoC.

Il modello di progettazione di Factory è piuttosto vecchio (in termini di Software) ed è in circolazione da un po 'di tempo. Dal momento che la recente popolarità del modello architettonico IoC sta avendo un risorgere.

Immagino quando si tratta di schemi di progettazione IoC: iniettori iniettati, localizzatori localizzati e refactoring delle fabbriche.


Quando si utilizza una fabbrica, il codice è ancora effettivamente responsabile della creazione di oggetti. Di DI esternalizzi questa responsabilità a un'altra classe oa un framework, che è separato dal tuo codice.


So che questa domanda è vecchia ma vorrei aggiungere i miei cinque centesimi,

Penso che l'injection dependance (DI) sia in molti modi simile a un Factory Pattern (FP) configurabile, e in questo senso qualsiasi cosa tu possa fare con DI sarai in grado di farlo con tale factory.

In realtà, se usi Spring, ad esempio, hai la possibilità di autowiring resources (DI) o qualcosa del genere:

MyBean mb = ctx.getBean("myBean");

E poi usa quell'istanza 'mb' per fare qualsiasi cosa. Non è una chiamata ad una fabbrica che ti restituirà un'istanza ??

L'unica vera differenza che noto tra la maggior parte degli esempi FP è che puoi configurare cosa "myBean" è in un xml o in un'altra classe, e un framework funzionerà come la fabbrica, ma a parte questo è la stessa cosa, e tu può avere certamente una Factory che legge un file di configurazione o ottiene l'implementazione di cui ha bisogno.

E se mi chiedi la mia opinione (e so che non l'hai fatto), credo che DI fa la stessa cosa ma aggiunge solo più complessità allo sviluppo, perché?

beh, per prima cosa, perché tu sappia qual è l'implementazione utilizzata per qualsiasi bean che si autowire con DI, devi andare alla configurazione stessa.

ma ... che dire della promessa che non dovrai conoscere l'implementazione dell'oggetto che stai usando? pfft! sul serio? quando usi un approccio come questo ... non sei lo stesso che scrive l'implementazione ?? e anche se non lo fai, stai quasi sempre guardando come l'implementazione fa quello che dovrebbe fare ??

e per un'ultima cosa, non importa quanto un framework DI ti prometta che costruirai le cose disaccoppiate da esso, senza dipendenze dalle loro classi, se stai usando un framework tu costruisci tutto ciò che lo circonda, se devi cambiare l'approccio o il quadro non sarà un compito facile ... MAI! ... ma, dal momento che costruisci tutto intorno a quel particolare framework invece di preoccuparti di quale sia la soluzione migliore per la tua azienda, allora dovrai affrontare un problema con biiig quando lo fai.

In effetti, l'unica vera applicazione aziendale per un approccio FP o DI che posso vedere è se è necessario modificare le implementazioni utilizzate in fase di esecuzione , ma almeno i framework che conosco non consentono di farlo, devi partire tutto perfetto nella configurazione in fase di sviluppo e se ne hai bisogno usa un altro approccio.

Quindi, se ho una classe che si comporta in modo diverso in due ambiti nella stessa applicazione (diciamo, due aziende di un'azienda) devo configurare il framework per creare due bean diversi e adattare il mio codice per utilizzarli. Non è come se scrivessi qualcosa del genere:

MyBean mb = MyBeanForEntreprise1(); //In the classes of the first enterprise
MyBean mb = MyBeanForEntreprise2(); //In the classes of the second enterprise

lo stesso di questo:

@Autowired MyBean mbForEnterprise1; //In the classes of the first enterprise
@Autowired MyBean mbForEnterprise2; //In the classes of the second enterprise

E questo:

MyBean mb = (MyBean)MyFactory.get("myBeanForEntreprise1"); //In the classes of the first enterprise
MyBean mb = (MyBean)MyFactory.get("myBeanForEntreprise2"); //In the classes of the second enterprise

In ogni caso dovrai modificare qualcosa nella tua applicazione, sia essa classi o file di configurazione, ma dovrai eseguirne il ridistribuzione.

Non sarebbe bello fare solo qualcosa del genere:

MyBean mb = (MyBean)MyFactory.get("mb"); 

E in questo modo, si imposta il codice della fabbrica per ottenere l'implementazione corretta in fase di esecuzione a seconda dell'impresa utente registrata ?? Ora sarebbe utile. Potresti semplicemente aggiungere un nuovo jar con le nuove classi e impostare le regole magari anche in fase di runtime (o aggiungere un nuovo file di configurazione se lasci questa opzione aperta), nessuna modifica alle classi esistenti. Questa sarebbe una fabbrica dinamica!

non sarebbe più utile che dover scrivere due configurazioni per ogni impresa, e magari anche avere due diverse applicazioni per ciascuna?

Puoi dirmi che non ho bisogno di fare lo switch in runtime, quindi configuro l'app, e se eredito la classe o uso un'altra implementazione, cambio semplicemente la configurazione e ridistribuisci. Ok, questo può anche essere fatto con una fabbrica. E sii onesto, quante volte lo fai? forse solo quando hai un'app che verrà utilizzata da qualche altra parte nella tua azienda e passerai il codice a un'altra squadra, e faranno cose del genere. Ma hey, che può anche essere fatto con la fabbrica, e sarebbe ancora meglio con una fabbrica dinamica !!

Ad ogni modo, la sezione dei commenti se è aperta per te per uccidermi.


Un Injection Framework è un'implementazione del modello di fabbrica.

Tutto dipende dalle tue esigenze. Se hai bisogno di implementare lo schema di fabbrica in un'applicazione, è molto probabile che i tuoi requisiti saranno soddisfatti da una delle innumerevoli implementazioni di framework di iniezione disponibili.

Dovresti implementare la tua soluzione solo se le tue esigenze non possono essere soddisfatte da nessuno dei framework di terze parti. Più codice scrivi, più codice devi mantenere. Il codice è una responsabilità, non un bene.

Argomenti su quale implementazione si dovrebbe usare non è di fondamentale importanza come capire le esigenze architettoniche della vostra applicazione.


Uno svantaggio di DI è che non può inizializzare oggetti con la logica. Ad esempio, quando ho bisogno di creare un personaggio che ha un nome e un'età casuali, DI non è la scelta rispetto al modello di fabbrica. Con le fabbriche, possiamo facilmente incapsulare l'algoritmo casuale dalla creazione dell'oggetto, che supporta uno dei modelli di progettazione chiamati "Incapsula ciò che varia".


In termini semplici, il metodo Dipendenza Iniezione vs Fabbrica implica rispettivamente il meccanismo push-pull.

Con il meccanismo di pull: le classi dipendono indirettamente dal Metodo Factory che a sua volta ha dipendenza dalle classi concrete.

Con meccanismo Push: il componente root può essere configurato con tutti i componenti dipendenti in un'unica posizione, favorendo così un'elevata manutenzione e un accoppiamento lento.

Con il metodo Factory, la responsabilità sta ancora nella classe (anche se indirettamente) per creare un nuovo oggetto dove, come con l'iniezione della dipendenza, la responsabilità viene esternalizzata (al costo però di perdere l'astrazione)


Iniezione di dipendenza

Invece di istanziare le parti stesse, un'automobile richiede le parti di cui ha bisogno per funzionare.

class Car
{
    private Engine;
    private SteeringWheel;
    private Tires tires;

    public Car(Engine engine, SteeringWheel wheel, Tires tires)
    {
        this.Engine = engine;
        this.SteeringWheel = wheel;
        this.Tires = tires;
    }
}

Fabbrica

Mette insieme i pezzi per creare un oggetto completo e nasconde il tipo concreto dal chiamante.

static class CarFactory
{
    public ICar BuildCar()
    {
        Engine engine = new Engine();
        SteeringWheel steeringWheel = new SteeringWheel();
        Tires tires = new Tires();
        ICar car = new RaceCar(engine, steeringWheel, tires);
        return car;
    }   
}

Risultato

Come puoi vedere, Fabbriche e DI si completano a vicenda.

static void Main()
{
     ICar car = CarFactory.BuildCar();
     // use car
}

Ti ricordi i riccioli d'oro e i tre orsi? Bene, l'iniezione di dipendenza è un po 'come quella. Qui ci sono tre modi per fare la stessa cosa.

void RaceCar() // example #1
{
    ICar car = CarFactory.BuildCar();
    car.Race();
}

void RaceCar(ICarFactory carFactory) // example #2
{
    ICar car = carFactory.BuildCar();
    car.Race();
}

void RaceCar(ICar car) // example #3
{
    car.Race();
}

Esempio n. 1 - Questo è il peggiore perché nasconde completamente la dipendenza. Se guardassi il metodo come una scatola nera non avresti idea di aver bisogno di un'auto.

Esempio 2 - Questo è un po 'meglio perché ora sappiamo che abbiamo bisogno di una macchina da quando passiamo in una fabbrica di automobili. Ma stavolta stiamo passando troppo perché tutto il metodo effettivamente necessario è un'auto. Stiamo passando in una fabbrica solo per costruire la macchina quando l'auto potrebbe essere costruita al di fuori del metodo e inoltrata.

Esempio n. 3 : ideale perché il metodo richiede esattamente ciò di cui ha bisogno. Non troppo o troppo poco. Non devo scrivere un MockCarFactory solo per creare MockCars, posso passare direttamente la simulazione. È diretto e l'interfaccia non mente.

Questo Google Tech Talk di Misko Hevery è sorprendente ed è la base di ciò da cui ho tratto il mio esempio. http://www.youtube.com/watch?v=XcT4yYu_TTs


Io uso entrambi per creare una strategia di Inversion Of Control con una maggiore leggibilità per gli sviluppatori che hanno bisogno di mantenerla dopo di me.

Uso una Factory per creare i miei oggetti Layer (Business, Data Access).

ICarBusiness carBusiness = BusinessFactory.CreateCarBusiness();

Un altro sviluppatore vedrà questo e quando crea un oggetto Business Layer che guarda in BusinessFactory e Intellisense offre allo sviluppatore tutti i possibili Business Layer da creare. Non è necessario giocare, trovare l'interfaccia che voglio creare.

Questa struttura è già Inversion Of Control. Non sono più responsabile della creazione dell'oggetto specifico. Ma hai ancora bisogno di assicurare l'iniezione di dipendenza per essere in grado di cambiare le cose facilmente. Creare la propria Iniezione delle Dipendenze personalizzata è ridicolo, quindi utilizzo Unity. All'interno di CreateCarBusiness () chiedo a Unity di stabilire quale classe appartiene a questo ed è vita.

Quindi il mio codice Struttura di dipendenza della dipendenza di fabbrica è:

public static class BusinessFactory
{
    public static ICarBusiness CreateCarBusiness()
    {
       return Container.Resolve<ICarBusiness>();
    }
}

Ora ho il vantaggio di entrambi. Il mio codice è anche più leggibile per altri sviluppatori rispetto agli scope dei miei oggetti che uso, invece di Constructor Dependency Injection che dice solo che ogni oggetto è disponibile quando la classe viene creata.

Lo uso per modificare l'accesso ai dati del database a un livello di accesso ai dati codificato personalizzato quando creo i test delle unità. Non voglio che i miei Test unitari comunichino con database, server web, server di posta elettronica, ecc. Devono testare il mio Business Layer perché è lì che si trova l'intelligenza.


Penso che questi siano ortogonali e possano essere usati insieme. Lascia che ti mostri un esempio che di recente mi sono imbattuto nel lavoro:

Stavamo usando il framework Spring in Java per DI. Una classe singleton ( Parent) doveva istanziare nuovi oggetti di un'altra classe ( Child), e quelli avevano collaboratori complessi:

@Component
class Parent {
    // ...
    @Autowired
    Parent(Dep1 dep1, Dep2 dep2, ..., DepN depN) {
        this.dep1 = dep1;
        this.dep2 = dep2;
    }

    void method(int p) {
        Child c = new Child(dep1, dep2, ..., depN, p);
        // ...
    }
}

In questo esempio, Parentdeve ricevere DepXistanze solo per passarle al Childcostruttore. Problemi con questo:

  1. Parentha più conoscenza di Childquanto dovrebbe
  2. Parent ha più collaboratori di quanto dovrebbe
  3. L'aggiunta di dipendenze Childcomporta il cambiamentoParent

Questo è quando ho capito che si Factorysarebbe adattato perfettamente qui:

  1. Nasconde tutto tranne i veri parametri della Childclasse, come visto daParent
  2. Incapsula la conoscenza della creazione di una Child, che può essere centralizzata nella configurazione DI.

Questa è la Parentclasse semplificata e la ChildFactoryclasse:

@Component
class Parent {
    // ...
    @Autowired
    Parent(ChildFactory childFactory) {
        this.childFactory = childFactory;
    }

    void method(int p) {
        Child c = childFactory.newChild(p);
        // ...
    }
}

@Component
class ChildFactory {
    // ...
    @Autowired
    Parent(Dep1 dep1, Dep2 dep2, ..., DepN depN) {
        this.dep1 = dep1;
        this.dep2 = dep2;
        // ...
        this.depN = depN;
    }

    Child newChild(int p) {
        return new Child(dep1, dep2, ..., depN, p);
    }
}






design-patterns