2015-06-11 14 views
14

Diciamo che abbiamo la classe A nel pacchetto A e la classe B nel pacchetto B. Se l'oggetto della classe A ha riferimento alla classe B, allora si dice che le due classi abbiano un accoppiamento tra di esse.Che cosa significa disaccoppiare due classi a livello di interfaccia?

Per indirizzare l'accoppiamento, si consiglia di definire un'interfaccia nel pacchetto A che è implementata dalla classe nel pacchetto B. Quindi l'oggetto della classe A può fare riferimento all'interfaccia nel pacchetto A. Questo è spesso un esempio di "inversione di dipendenza".

È questo l'esempio di "disaccoppiamento di due classi a livello di interfaccia". Se sì, come rimuove l'accoppiamento tra classi e mantiene la stessa funzionalità quando due classi sono state accoppiate?

+2

Consiglio vivamente le pubblicazioni di Robert C. Martin sulle regole SOLID della programmazione orientata agli oggetti. Ecco alcuni dettagli sul tuo problema: http://www.objectmentor.com/resources/articles/dip.pdf –

risposta

29

Cerchiamo di creare un esempio fittizio.

Classe A nel pacchetto packageA:

package packageA; 

import packageB.B; 

public class A { 
    B myB; 

    public A() { 
     this.myB = new B(); 
    } 

    public void doSomethingThatUsesB() { 
     System.out.println("Doing things with myB"); 
     this.myB.doSomething(); 
    } 
} 

Classe B nel pacchetto packageB:

package packageB; 

public class B { 
    public void doSomething() { 
     System.out.println("B did something."); 
    } 
} 

Come si vede, A dipende B. Senza B, non è possibile utilizzare A. Ma cosa, se vogliamo sostituire B in futuro con un BetterB? Ora, iniziamo a creare un'interfaccia Inter entro packageA:

package packageA; 

public interface Inter { 
    public void doSomething(); 
} 

Per utilizzare questa interfaccia, abbiamo import packageA.Inter; e lasciare B implements Inter in B e sostituire tutte le occorrenze di B all'interno A con Inter. Il risultato è questa versione modificata del A:

package packageA; 

public class A { 
    Inter myInter; 

    public A() { 
     this.myInter = ???; // What to do here? 
    } 

    public void doSomethingThatUsesInter() { 
     System.out.println("Doing things with myInter"); 
     this.myInter.doSomething(); 
    } 
} 

A questo punto, si vede già che la dipendenza A-B è andato: la import packageB.B; non è più necessario. C'è solo un problema: non possiamo istanziare un'istanza di un'interfaccia. Ma Inversion of control viene in soccorso. Invece di istanziare qualcosa di tipo Inter wihtin costruttore A s', noi chiederemo qualcosa che implements Inter come parametro per il costruttore:

package packageA; 

public class A { 
    Inter myInter; 

    public A(Inter myInter) { 
     this.myInter = myInter; 
    } 

    public void doSomethingThatUsesInter() { 
     System.out.println("Doing things with myInter"); 
     this.myInter.doSomething(); 
    } 
} 

Con questo approccio possiamo ora modificare l'implementazione concreta di Inter all'interno A a volontà. Supponiamo di scrivere una nuova classe di BetterB:

package packageB; 

import packageA.Inter; 

public class BetterB implements Inter { 
    @Override 
    public void doSomething() { 
     System.out.println("BetterB did something."); 
    } 
} 

Ora possiamo instantiante A s con diverse implementazioni Inter:

Inter b = new B(); 
A aWithB = new A(b); 
aWithB.doSomethingThatUsesInter(); 

Inter betterB = new BetterB(); 
A aWithBetterB = new A(betterB); 
aWithBetterB.doSomethingThatUsesInter(); 

e non abbiamo dovuto cambiare nulla all'interno A. Il codice è ora disaccoppiato ed è possibile modificare l'implementazione concreta di Inter a piacere, purché il (i) contratto (i) di Inter sia (sono) soddisfatto. In particolare, è possibile supportare il codice, che verrà generato in futuro e implementa Inter.

+1

+1 Inversione del controllo viene in soccorso. Questo è il miglior disaccoppiamento e inversione della relazione di controllo che abbia mai visto. E ho letto libri su questo argomento. –

+0

Molto ben spiegato ..... reso il concetto più chiaro per me –

+0

Ma la classe A non dipende ora dall'interfaccia? L'hai appena disaccoppiato da B e l'hai collegato all'interfaccia giusto? Come va meglio? – 11m0

3

La situazione descritta rimuove la dipendenza che la classe A ha sull'implementazione specifica della classe B e la sostituisce con un'interfaccia. Ora la classe A può accettare qualsiasi oggetto che sia di un tipo che implementa l'interfaccia, invece di accettare solo la classe B. Il design mantiene la stessa funzionalità poiché la classe B è realizzata per implementare tale interfaccia.

4

Immaginate che la funzionalità di B sia quella di scrivere un registro in qualche database. La classe B dipende dalla funzionalità della classe DB e fornisce un'interfaccia per le sue funzionalità di registrazione ad altre classi.

Classe A richiede la funzionalità di registrazione di B, ma non importa, dove viene scritto il registro. Non si cura per DB, ma poiché dipende da B, dipende anche da DB. Questo non è molto desiderabile.

Che cosa si può fare, è quello di dividere la classe B in due classi: una classe astratta L che descrive la funzionalità di registrazione (e non a seconda DB), e l'implementazione a seconda DB.

Quindi è possibile disaccoppiare la classe A da B, perché ora A dipenderà solo da L. B ora dipende anche da L, motivo per cui è chiamato inversione di dipendenza, perché B fornisce la funzionalità offerta in L.

Poiché A ora dipende solo da un sottile L, è possibile utilizzarlo facilmente con altri meccanismi di registrazione, non in base a DB. Per esempio. è possibile creare un semplice logger basato su console, implementando l'interfaccia definita in L.

Ma poiché ora A non dipende B ma (nelle fonti) solo sull'interfaccia astratto L in fase di esecuzione deve essere configurato per utilizzare una specifica implementazione di L (B per esempio). Quindi ci deve essere qualcun altro che dice a A di usare B (o qualcos'altro) durante il runtime. E questo è chiamato inversione di controllo, perché prima A ha deciso di utilizzare B, ma ora qualcun altro (ad esempio un contenitore) dice A per utilizzare B durante il runtime.