Iniezione delle dipendenze o ricerca delle dipendenze in un'architettura a più livelli

2

Data un'architettura a livelli, in cui la logica aziendale è isolata in un pacchetto da tutti gli altri livelli, come memoria persistente, interfaccia utente, interfacce per vari servizi remoti (fornitore), ecc., sto pensando di gestire le dipendenze nel modo seguente :

All'interno della biblioteca, tutte le dipendenze vengono iniettate manualmente tramite il costruttore. Questo ha il vantaggio (che a mio parere è grande) che rende i test più espliciti, aumentandone il valore come fonte di documentazione.

Per gli altri livelli e plugin, sto pensando di iniettare manualmente solo un oggetto usato per la ricerca delle dipendenze, il che renderebbe più semplice il mocking. Dubito che ci sarebbe un solo oggetto per tutti i sottosistemi, sto semplicemente pensando a interfacce realmente specifiche, piccole e factory che implementano una o più di queste interfacce. Quante interfacce implementano una fabbrica è un dettaglio di implementazione, immagino che inizierei con una sola fabbrica che implementa tutte le interfacce e man mano che il sistema si evolve, estraggerei di conseguenza altre fabbriche e refactoring (YAGNI). Le interfacce (vale a dire la firma dei costruttori) rimarrebbero com'erano prima del refactoring, grazie a interface segregation .

La biblioteca aziendale riceve i suoi comandi dall'esterno tramite i comandi (pensa DDD).

  1. Cosa ne pensi di questo? Altri vantaggi e svantaggi sono ben accetti!
  2. Quali altre strategie per la gestione delle dipendenze preferisci in quali situazioni e perché?
posta Flavius 18.04.2015 - 11:46
fonte

2 risposte

2

Si sta per qualcosa di simile a (ma non simile) Spring ApplicationContextAware , dove si passa ApplicationContext (che è un BeanFactory) tramite un setter, quindi in un secondo momento può ottenere i bean di cui ha bisogno.

Generalmente è una cattiva idea, se hai solo bisogno di iniettare le dipendenze:

  • Non è esplicito sulle dipendenze che usa. Quindi, in realtà più difficile per deridere, non è più facile, perché non sei sicuro di cosa debba essere deriso a meno che non guardi nel codice intero di una classe e probabilmente il intero codice delle sue "super- e sottoclassi, se si verifica l'ereditarietà.
  • Non è sicuro da testo se si interrogano le dipendenze con chiavi stringa, con tutte le conseguenze negative. Tuttavia, se dici che avrai a che fare con interfacce specifiche (come UserRepositoryFactory che ha un metodo come getUserRepository , suppongo), sarà sicuro, ma non vedo il vantaggio in questa soluzione , perché sembra molto più facile iniettare semplicemente dipendenze senza oggetti getter / wrapper / injector medi. Penso che sarebbe solo la duplicazione della funzionalità DI.

Se realizzassi un sistema di plugin, andrei con plugin "stupidi", che implementano solo un'interfaccia SomethingPlugin , e mi aspetto che le sue dipendenze vengano iniettate (tramite costruttore o setter, che non ha molta importanza) di il sistema, senza alcuna conoscenza preliminare di nulla al di fuori.

Esempio:

public class UserRatingPlugin implements GenericPlugin,
        UserRepositoryAwarePlugin, PostResositoryAwarePlugin {

    // here are dependencies
    private UserRepository userRepository;
    private PostRepository postRepository

    @Override // from UserRepositoryAwarePlugin
    public void setUserRepository(UserRepository ur) {
        userRepository = ur;
    }

    @Override // from PostResositoryAwarePlugin
    public void setPostRepository(PostRepository pr) {
        postRepository = pr;
    }

    @Override // from GenericPlugin
    // this should be required to make this class a plugin
    public void doPluginMagic() { /* ... */ }
}

Quindi si registra questo plugin (tramite il file di configurazione, immagino), quindi da qualche parte in un codice di "livello di sistema" si istanzia questa classe, si impostano le dipendenze (questa parte può essere automatizzata da Spring o un simile DI-contenitore) e si invoca% % co_de.

In questo modo hai ancora interfacce specifiche di piccole dimensioni, ma ora come parti di un'API di plugin, che credo semplifichi il tutto. Puoi vedere a quale plug-in può accedere semplicemente guardando la sua "dichiarazione di tipo" e non è necessario creare ulteriori "fabbriche".

    
risposta data 18.04.2015 - 13:37
fonte
3

La ricerca delle dipendenze sembra molto simile al modello del localizzatore di servizi, ma spesso, il localizzatore di servizio è statico.

I vantaggi di tale ricerca di dipendenza rispetto all'iniezione di dipendenza ordinaria sono i seguenti:

  • Non troverai costruttori che accettano dozzine di argomenti (spesso obbligatori) se hanno bisogno di molte dipendenze. Questo rende il tuo codice più breve e più facile da leggere e scrivere.

  • Non è necessario modificare la definizione del costruttore quando l'oggetto deve fare affidamento su una dipendenza aggiuntiva (o non ha bisogno di una dipendenza utilizzata in precedenza). Ciò significa meno modifiche al codice nel tempo.

C'è tuttavia uno svantaggio:

  • Le dipendenze sono fuori controllo. Quando le dipendenze vengono iniettate tramite la funzione di costruzione, osservando il costruttore, si viene immediatamente a conoscenza delle dipendenze richieste dalla classe. Se l'unica cosa che esiste è una ricerca (o un localizzatore di servizi), sai solo che la classe si basa su una dipendenza da N , senza ulteriori informazioni.

    Questo rende anche difficile l'uso di quelle classi. Immagina di essere nel contesto di un test unitario. Quali oggetti, nella ricerca, dovrebbero essere derisi? Il problema esiste già per l'integrazione delle dipendenze (poiché il metodo testato non deve necessariamente utilizzare ogni dipendenza richiesta dalla classe), ma con una ricerca diventa più grande, con più dipendenze che possono essere potenzialmente utilizzate.

    Pass-through (ovvero quando una classe A richiede una dipendenza per passarla a un'altra classe B che chiama) può rendere questa attività ancora peggiore, poiché non hai visibilità su quali dipendenze vengono utilizzate dalla classe B se passi la ricerca ad esso.

risposta data 18.04.2015 - 12:33
fonte