2010-10-26 16 views
9

Sto progettando un piccolo progetto in cui non ho usato la programmazione contro le interfacce per tutte le mie classi. Ho scoperto che alcune classi non sarebbero quasi mai necessarie per cambiare, quindi li lascio fare riferimento alle loro classi client come classi concrete.Devo rendere esplicite le dipendenze delle classi concrete mediante l'iniezione del costruttore nelle mie classi?

Quindi, cerchiamo di dire che abbiamo ClassB che sarà consumato essere consumato da ClassA:

alt text

class ClassB { 
} 

La mia domanda è, devo creare ClassB in ClassA, o dovrei passare che responsabilità "su" nella gerarchia? Io rappresento entrambi i casi:

class ClassA { 
    private ClassB classB; 

    public ClassA() { 
     this.classB = new ClassB(); 
    } 
} 

o

class ClassA { 
    private ClassB classB; 

    public ClassA(ClassB classB) { 
     this.classB = classB; 
    } 
} 

mi piacerebbe sentir parlare di come si dovrebbe fare e Cos'hai essere la logica dietro di esso!

Grazie

+1

non fa differenza! Stai ancora programmando sul cemento! –

+0

Effettivamente. Posso passare le lezioni estese di ClassB. Ma allora forse avrebbe più senso programmare semplicemente ClassA contro un'interfaccia, in primo luogo. :( –

+0

Sì .................. –

risposta

3

Alcuni vantaggi della vostra prima opzione (A crea B):

  1. L'utente di classe A ha bisogno di sapere nulla di B o come creare & configurarlo. Questo rende più semplice il modello di programmazione.
  2. B può anche essere reso privato/interno, in modo che gli utenti della tua biblioteca non debbano guadare attraverso molte classi di helper che non hanno bisogno di vedere.
  3. È possibile modificare il modo in cui A funziona per non utilizzare affatto B senza rompere il codice chiamante.
  4. incapsulamento - nessuno si faccia di manomettere B dall'esterno mentre A è in esecuzione

Alcuni vantaggi della vostra seconda opzione (A prende una dipendenza da B):

  1. L'utente di classe A ha il pieno controllo sull'oggetto B - possono configurarlo in modo diverso, possono passare lo stesso in più istanze di A se lo desiderano.
  2. Si apre la porta per diverse implementazioni da passare in (se B è un'interfaccia o una classe di base) che è di grande valore per unit test, o estensibilità.
  3. aggiornamento: se in futuro, diventa più complicato per creare B (per esempio B ha alcune dipendenze della propria), che poi può essere gestito al di fuori senza la necessità di apportare modifiche al A.

si noti che è anche possibile creare un "meglio dei due mondi" soluzione utilizzando ciò che è talvolta chiamato "l'iniezione di dipendenza dei poveri":

class ClassA { 
    private ClassB classB; 

    public ClassA(ClassB classB) { 
     this.classB = classB; 
    } 

    public ClassA() : this(new ClassB()) { 

    } 
} 
3

Dipende l'attuazione, non c'è una risposta generale. Se A ha tutti i dati e le conoscenze rilevanti per inizializzare B, allora potrebbe inizializzarlo.

Se non si desidera che A sappia come inizializzare B o non si desidera fornire A tutti i dati per l'inizializzazione, è necessario crearlo esternamente.

+0

Sembra una buona euristica da seguire. –

2

è consigliabile inserire l'interfaccia implementata da ClassB.

class ClassA { 
private InterfaceB B; 

public ClassA(InterfaceB B) { 
    this.B = B; 
} 
} 
class classB: InterfaceB 
{ 
} 

Si prega di notare che InterfaceB può essere una classe regolare come classe base per i vostri derivati. In questo caso funziona ancora come interfaccia.

+4

Sì, ma penso che manchi il mio punto. Non voglio gonfiare il mio codice con fondamentalmente 1 interfaccia per ogni classe concreta se non avrò bisogno di tanta flessibilità. Forse sbaglio pensando in questo modo? Sto solo cercando di bilanciare il progetto "complessità" e flessibilità. –

+0

@devita bene quando l'iniezione non fa differenza. Sarebbe come se classB fosse un membro privato. L'idea di DI deve essere indipendente dalla realizzazione concreta di classi dipendenti. – Arseny

+0

Per utilizzare un'interfaccia invece di una classe concreta non si intende far saltare la complessità della propria implementazione. Ma ti permetterà di essere più flessibile. In questo modo, non si crea dipendenza tra le classi che è un inferno al refactoring in seguito. – Arthis

1

Nel primo A si assume la piena responsabilità della classe B. Quindi, non c'è nulla che possa andare storto. A conosce lo B come qualsiasi cosa. Nel caso in cui A desideri impostare alcune delle proprietà B prima di richiamare qualsiasi metodo su di esso. Può farlo impostando quelli subito dopo l'intializzazione.

In quest'ultimo, A non può essere sicuro di B.

Ora, la scelta è ovviamente la vostra.

0

un paio di vantaggi per la seconda opzione (passando l'oggetto attraverso il costruttore, cioè controllo inverso) che non è stato menzionato finora:

1) È possibile utilizzare uno strumento iniezione di dipendenza per iniettare l'oggetto ClassB. Anche se non può iniettare diverse implementazioni perché si sono bloccati alla classe di cemento, si può specificare l'oggetto del ciclo di vita (Singleton, per-richiesta, per-filo) ed eventuali parametri del costruttore utilizzati per creare un'istanza dell'oggetto.

2) Se si passa sempre dipendenze nel costruttore (inversione di controllo), allora è senza dubbio più chiaro che dipendenze ogni classe è a colpo d'occhio.

Problemi correlati