Utilizzo dell'iniezione delle dipendenze utilizzando il modello di fabbrica

2

Voglio creare un .jar che incapsula un API del fornitore, quindi possiamo usare i nostri oggetti come parametri per comunicare con l'API.

Ho letto alcuni articoli e argomenti qui in SO, ma sono ancora un po 'confuso se ottengo l'iniezione di dipendenza giusta. Per esempi di House oggetti con Doors e Windows , è facile da capire, ma sembra che diventi difficile con il codice reale.

Non volevo utilizzare alcun modello framework / locator. Il seguente codice è solo una semplificazione di come gli oggetti sono effettivamente. L'oggetto Cake è un oggetto grande e così via:)

Questo codice è valido? Dovrei cambiarlo per essere più facile da testare?

//These are the third-party classes
class VendorService {
    public VendorService(String wsdlPath) {/*vendor code*/}
    ICakeApi getApi();
}

interface ICakeApi {
    void authenticate(String username, String password);
    VendorSpecificCake cookCake(VendorSpecificIngredients ingredients);
}
//This is the code I'm trying to use DI
class MyCakeService {
    ICakeApi cakeApi;

    public MyCakeService(ICakeApi cakeApi) {
        this.cakeApi = cakeApi;
    }

    public void authenticate(MyUserPasswordBean bean) {
        cakeApi.authenticate(bean.getUsername(), bean.getPassword());
    }

    MySpecificCake cookCake(MySpecificIngredients ingredients, 
                            VendorObjectFactory vendorFactory, 
                            InternalObjectFactory internalFactory) {

        VendorSpecificIngredients objs =
            vendorFactory.createVendorSpecificIngredients(ingredients);

        VendorSpecificCake vCake = cakeApi.cookCake(objs);
        MySpecificCake myCake = internalFactory.createMySpecificCake(vCake);
        return myCake;
    }
}

class MyCakeServiceFactory {

    MyCakeService build(String wsdlPath) {
        VendorService vendorService = new VendorService(wsdlPath);
        ICakeApi cakeApi = vendorService.getApi();
        MyCakeService service = new MyCakeService(cakeApi);
    }
}
class UsageTest {
    public void testMyCode() {
        MyCakeServiceFactory factory = new MyCakeServiceFactory();

        //should I add this as dependency on the constructor?
        VendorObjectFactory vendorFactory = new VendorObjectFactory();

        InternalObjectFactory internalFactory = new InternalObjectFactor();
        MyCakeService service = factory.build("/tmp");
        service.authenticate(new MyUserPasswordBean("john", "snow"));

        MySpecificIngredients ingr = new MySpecificIngredients(...);

        //ideally, I'd like to avoid having the user to instantiate 
        //VendorObjectFactory and InternalObjectFactory
        service.cookCake(ingr, vendorFactory, internalFactory); 
    }
}
    
posta Felipe S. 21.07.2016 - 04:04
fonte

1 risposta

1

I didn't want to use any framework / locator pattern

Capisco questo, infatti lo rispetto. Ma significa che il tuo principale sarà un posto occupato, a meno che tu non faccia buon uso di schemi creativi. Le buone impostazioni predefinite possono consentire la convenzione sulla configurazione per semplificare le cose. Sorprendentemente, questo si riduce alla presentazione degli esseri umani con metodi semplici per usare le classi. Questo non succede solo. Questo è lavoro.

Is this code fine? Should I change it in order to be easier to test?

Non vedo nulla di difficile da testare qui. cookCake() si riduce alla composizione funzionale e potrei iniettare le mie proprie funzioni (metodi well in realtà) in qualsiasi momento. Potrei certamente iniettare mock se necessario.

//should I add this as dependency on the constructor?
VendorObjectFactory vendorFactory = new VendorObjectFactory();

C'è sempre una buona ragione per cambiare da una chiamata all'altra? Può essere soddisfatta questa ragione semplicemente creando due diverse istanze MyCakeService ? Cosa è più leggibile?

//ideally, I'd like to avoid having the user to instantiate 
//VendorObjectFactory and InternalObjectFactory
service.cookCake(ingr, vendorFactory, internalFactory);

Ci sono buoni valori predefiniti per questi? Se la maggior parte delle volte le tue usano le stesse, rendili i valori predefiniti.

Quando inizi a seguire le idee di dipendenza, i valori predefiniti per l'iniezione possono sembrare fuori luogo. Finché posso iniettare qualcosa per sostituire il valore predefinito, questo non è fondamentale.

Ad alcune persone piace vietare completamente new negli oggetti comportamentali. È possibile seguire questo e ancora mettere la convenzione sulla configurazione. Devi semplicemente spostare le impostazioni predefinite nel tuo builder.

L'idea è di rendere i tuoi costruttori molto facili da usare per gli umani. Ora gli oggetti comportamentali possono avere costruttori lunghi e dolorosi perché solo i costruttori si occupano di loro. Se gli umani passano la maggior parte del loro tempo usando i costruttori a cui importa?

Il Josh builder schiaccia queste due idee insieme per evitare di avere sempre un costruttore lungo. Ma tu non devi farlo in quel modo. Piuttosto che farlo con una classe interiore e una classe esterna, puoi semplicemente utilizzare due classi complete.

La grande cosa che fa il costruttore Josh in realtà non ci sta liberando di lunghi costruttori. Ci fornisce parametri con nome in lingue che non li hanno. I parametri con nome rendono il prelievo e la scelta di quali valori predefiniti possono essere sovrascritti in modo molto semplice. Ciò semplifica la messa in convenzione della configurazione.

E ciò significa no, gli utenti non devono istanziare VendorObjectFactory e InternalObjectFactory a mano. È possibile offrire un builder che li costruisca come valori predefiniti, ma facilita l'inserimento di sostituzioni per essi. E il comportamento oggetto prodotto può persino essere immutabile.

Dovrai accordare questo ai tuoi gusti ma questo ha funzionato bene per me. È un bel po 'di stazza, ma il risultato è la flessibilità senza essere sopraffatto dalle scelte.

    
risposta data 21.07.2016 - 06:07
fonte

Leggi altre domande sui tag