2015-06-11 6 views
14

Sagen wir, wir haben Klasse A in Paket A und Klasse B in Paket B. Wenn sich ein Objekt der Klasse A auf die Klasse B bezieht, dann wird gesagt, dass die zwei Klassen eine Kopplung zwischen ihnen haben.Was bedeutet die Entkopplung von zwei Klassen auf der Schnittstellenebene?

Um die Kopplung zu adressieren, wird empfohlen, eine Schnittstelle in Paket A zu definieren, die durch die Klasse in Paket B implementiert wird. Dann kann das Objekt der Klasse A auf die Schnittstelle in Paket A verweisen. Dies ist oft ein Beispiel für "Umkehrung der Abhängigkeit".

Ist dies das Beispiel der "Entkopplung von zwei Klassen auf der Schnittstellenebene". Wenn ja, wie entfernt es die Kopplung zwischen Klassen und behält die gleiche Funktionalität bei, wenn zwei Klassen gekoppelt wurden?

+2

Ich empfehle Robert C. Martin Veröffentlichungen über SOLID Regeln der objektorientierten Programmierung. Hier einige Details zu Ihrem Problem: http://www.objectoror.com/resources/articles/dip.pdf –

Antwort

29

Lassen Sie uns ein fiktives Beispiel erstellen.

Klasse A in Paket 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(); 
    } 
} 

Klasse B in Paket packageB:

package packageB; 

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

Wie Sie sehen, hängt A auf B. Ohne B kann A nicht verwendet werden. Aber was, wenn wir in Zukunft B durch eine BetterB ersetzen wollen? Nun starten wir eine Schnittstelle Inter innerhalb packageA zu erstellen:

package packageA; 

public interface Inter { 
    public void doSomething(); 
} 

diese Schnittstelle zu nutzen, wir import packageA.Inter; und lassen B implements Inter in B und ersetzen alle Vorkommen von B innerhalb A mit Inter. Das Ergebnis ist diese modifizierte Version von 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(); 
    } 
} 

An dieser Stelle sehen wir bereits, dass die Abhängigkeit A-B gegangen ist: die import packageB.B; nicht mehr benötigt wird. Es gibt nur ein Problem: Wir können keine Instanz einer Schnittstelle instanziieren. Aber Inversion of control kommt zur Rettung. Anstatt etwas vom Typ Inter wihtin A ‚s Konstruktor instanziiert wird, werden wir etwas, das implements Inter als Parameter für den Konstruktor fordern:

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(); 
    } 
} 

Mit diesem Ansatz, den wir nun die konkrete Umsetzung von Inter innerhalb A nach Belieben ändern können. Angenommen, Sie eine neue Klasse BetterB schreiben:

package packageB; 

import packageA.Inter; 

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

Jetzt können wir instantiante A s mit verschiedenen Inter Implementierungen innerhalb A

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

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

Und wir haben nichts zu ändern. Der Code ist jetzt entkoppelt und Sie können die konkrete Implementierung von Inter beliebig ändern, solange der/die Vertrag (e) von Inter erfüllt ist (sind). Vor allem können Sie Code unterstützen, der zukünftig generiert wird und Inter implementiert.

+1

+1 Inversion der Kontrolle kommt zur Rettung. Dies ist die beste Entkopplung und Inversion der Kontrollbeziehung, die ich je gesehen habe. Und ich habe Bücher über diese Themen gelesen. –

+0

Sehr gut erklärt ..... machte das Konzept für mich klarer –

+0

Aber hängt Klasse A nicht jetzt von der Schnittstelle ab? Du hast es einfach von B abgekoppelt und es an die Schnittstelle gekoppelt, oder? Wie ist das besser? – 11m0

3

Die von Ihnen beschriebene Situation entfernt die Abhängigkeit der Klasse A von der spezifischen Implementierung der Klasse B und ersetzt sie durch eine Schnittstelle. Jetzt kann Klasse A jedes Objekt akzeptieren, das von einem Typ ist, der die Schnittstelle implementiert, anstatt nur Klasse B zu akzeptieren. Das Design behält die gleiche Funktionalität bei, da Klasse B für die Implementierung dieser Schnittstelle erstellt wurde.

4

Stellen Sie sich vor, dass die Funktionalität von B ist, ein Protokoll in eine Datenbank zu schreiben. Die Klasse B hängt von der Funktionalität der Klasse DB ab und bietet eine Schnittstelle für ihre Protokollierungsfunktionen für andere Klassen.

Klasse A benötigt die Protokollierungsfunktionalität von B, aber es ist egal, wo das Protokoll geschrieben wird. Es kümmert sich nicht um DB, aber da es auf B ankommt, hängt es auch von DB ab. Dies ist nicht sehr wünschenswert.

Also, was Sie tun können, ist die Klasse B in zwei Klassen aufgeteilt: Eine abstrakte Klasse L die Logging-Funktionalität beschreiben (und nicht abhängig von DB) und die Umsetzung je nach DB.

Dann können Sie die Klasse A von B abkoppeln, da A nur noch von L abhängt. B ist jetzt auch abhängig von L, deshalb heißt es Abhängigkeitsinversion, weil B die in L angebotene Funktionalität bietet.

Da A jetzt nur noch von einem Lean L abhängt, können Sie es problemlos mit anderen Protokollierungsmechanismen verwenden, unabhängig von DB. Z.B. Sie können einen einfachen konsolenbasierten Logger erstellen, der die in L definierte Schnittstelle implementiert.

Aber da jetzt A hängt nicht von B sondern (in Quellen) nur auf die abstrakten Schnittstelle L zur Laufzeit es eingerichtet werden muss einige spezifische Implementierung von L (B zum Beispiel) zu verwenden. Es muss also jemand anders sein, der A anweist, während der Laufzeit B (oder etwas anderes) zu verwenden. Und das heißt Inversion der Kontrolle, weil vor A beschlossen, B zu verwenden, aber jetzt jemand anderes (z. B. ein Container) sagt AB während der Laufzeit zu verwenden.