Iniezione di una dipendenza che è un albero di dipendenze

4

È un modello o antipattern di iniettare una dipendenza che è un albero di dipendenze?

Ad esempio:

public class ClassExample
{
    private IContainer _container;

    public ClassExample(IContainer container)
    {
        _container = container;
    }
}

public interface IContainer
{
    IDependencyA DependencyA { get; set; }
    IDependencyB DependencyB { get; set; }
    IDependencyC DependencyC { get; set; }
}

public interface IDependencyA
{
}

public interface IDependencyB
{
}

public interface IDependencyC
{
}

L'esempio sopra potrebbe essere reso ancora più profondo (una struttura) - ad esempio, IDipendenzaA potrebbe contenere anche più dipendenze IDipendenzaA, IDipendenzaAB, ecc.

Qualcuno potrebbe utilizzare il metodo sopra per ridurre al minimo la duplicazione del codice quando si dispone di più punti in cui è necessario iniettare uno stesso set di servizi. Il codice mostrerà: container.DependencyA.DependencyAA.Method(...) in molti casi che lo rende più prolisso e meno ASCIUTTO.

L'uso del metodo sopra è un'idea buona o cattiva (modello / antipattern)? O quando è una buona idea, e quando è una brutta?

    
posta Răzvan Flavius Panda 29.11.2018 - 13:15
fonte

2 risposte

8

container.DependencyA.DependencyAA.Method(...)

Sembra che potrebbe essere una violazione Law of Demeter . Il problema non ha nulla a che fare con le dipendenze.

Il problema sta raggiungendo oggetti passati e violando la loro astrazione. Questo è un problema perché se anche un client scrive un codice come: container.DependencyA.DependencyAA.Method() allora l'intera catena è impostata su pietra. Non hai la libertà di costruirlo in altro modo. Se il client avesse fatto riferimento ad esso come depAA.Method() , avremmo avuto una certa flessibilità.

Questo non vuol dire che non vuoi mai vedere una catena di punti. Dopo tutto la legge di Demeter non è un esercizio di conteggio dei punti . Le catene di punti lunghe vanno bene quando ti è stato promesso che il percorso rimarrà sempre vero. Ad esempio, gli stream Java8 ti fanno scorrere insieme catene meravigliosamente lunghe. Va bene perché ci si aspetta che queste catene. Ti è stato promesso che non cambieranno.

Il problema si verifica quando gli autori dei clienti esplorano il codice base e ricuciscono insieme qualunque percorso si verifichi. Ora questa base di codice accuratamente disaccoppiata è riunita in una palla di fango perché qualcuno ha continuato a cercare ciò di cui aveva bisogno piuttosto che chiedere semplicemente che fosse consegnato a loro.

L'idea è che sia meglio per ogni oggetto avere alcuni amici con cui sa come parlare. Quegli amici hanno amici che sanno come parlare. Se parli presto con i tuoi amici amici, devi sapere come parlare con tutti. Questo è male se vogliamo essere in grado di implementare un cambiamento perché tutto finisce per essere influenzato dal cambiamento. Valore non sapendo molto.

Usa le facciate, l'astrazione e l'iniezione delle dipendenze per impedire ai clienti di sapere come il tuo grafico degli oggetti è cablato insieme e tu mantenga la libertà di collegarli insieme come meglio credi.

Se devi consentire catene, assicurati di disaccoppiare la catena dall'implementazione sottostante. Questi sono chiamati Lingue specifiche di dominio interne (iDSL). Sono molto potenti ma richiedono molto lavoro da preparare. Le catene consentite formano il mini linguaggio. Assicurati di poter sostituire l'implementazione dietro la catena.

    
risposta data 29.11.2018 - 13:43
fonte
1

Va bene a condizione che:

  1. L'insieme di dipendenze passato è un contenitore semplice,
  2. e la collezione di dipendenze appartiene veramente insieme,
  3. e i membri della collezione sono (per lo più) dipendenze reali della classe in cui viene iniettata la collezione.

Non lasciarti fuorviare dal Principio della Minima Conoscenza (più cripticamente noto come la Legge di Demetra): è del tutto appropriato che una classe sappia delle loro dipendenze, e quello di cui stai parlando è un modo per fornire tali dipendenze alla classe, non una struttura funzionale separata che dovrebbe essere timida riguardo ai suoi interni. Questo è il primo degli orientamenti sopra riportati: il tuo insieme di dipendenze dovrebbe essere semplicemente un insieme di dipendenze, una volta che la classe che hai iniettato ha il suo stato e comportamento indipendente, l'insieme di dipendenze passato diventa parte dell'implementazione di quello classe e la chiarezza della separazione è persa.

Tuttavia, confezionare insieme per convenienza può rapidamente trasformarsi in un monolite gonfiato che consente alle classi di ottenere tutto quanto negli interni degli altri facendo passare troppe dipendenze che semplicemente non hanno alcuna attività commerciale che acceda a molte delle classi incluse in l'insieme e un mezzo per evitare di dover pensare a quali dipendenze vengono iniettate. Dopotutto, se hai intenzione di farlo, puoi anche fare in modo che la tua dipendenza stabilisca un singleton e abbia finito; è effettivamente diventato un modo più rozzo di fare la stessa cosa. Questo è ciò che riguarda la seconda e la terza delle regole di cui sopra. Le dipendenze racchiuse insieme dovrebbero avere una connessione naturale, non semplicemente essere un insieme di cose che è facile da aggirare, il che garantisce che il set passato abbia un significato in sé. Dovresti inoltre assicurarti che la maggior parte degli oggetti che ricevono il set utilizzino la maggior parte degli elementi nell'insieme la maggior parte del tempo, altrimenti non stai iniettando dipendenze, stai solo fornendo l'accesso allo stato generale del programma.

    
risposta data 29.11.2018 - 14:58
fonte