Prinzip der Schnittstellentrennung in Java

1. Einleitung

In diesem Tutorial werden wir das Prinzip der Schnittstellentrennung, eines der SOLID-Prinzipien, diskutieren. Die Schnittstellentrennung, die das „I“ in „SOLID“ darstellt, bedeutet einfach, dass wir größere Schnittstellen in kleinere aufteilen sollten.

So wird sichergestellt, dass beim Implementieren von Klassen keine unerwünschten Methoden implementiert werden müssen.

2. Prinzip der Schnittstellentrennung

Dieses Prinzip wurde zuerst von Robert C. Martin definiert als: „ Kunden sollten nicht gezwungen werden, sich auf Schnittstellen zu verlassen, die sie nicht verwenden “.

Ziel dieses Prinzips ist es , die Nebenwirkungen der Verwendung größerer Schnittstellen zu verringern, indem Anwendungsschnittstellen in kleinere aufgeteilt werden . Es ähnelt dem Prinzip der Einzelverantwortung, bei dem jede Klasse oder Schnittstelle einem einzigen Zweck dient.

Präzises Anwendungsdesign und korrekte Abstraktion sind der Schlüssel hinter dem Prinzip der Schnittstellentrennung. Obwohl dies in der Entwurfsphase einer Anwendung mehr Zeit und Mühe kostet und die Codekomplexität erhöhen kann, erhalten wir am Ende einen flexiblen Code.

Wir werden uns in den späteren Abschnitten einige Beispiele ansehen, bei denen ein Verstoß gegen das Prinzip vorliegt, und dann das Problem beheben, indem wir das Prinzip korrekt anwenden.

3. Beispielschnittstelle und Implementierung

Schauen wir uns eine Situation an, in der wir eine Zahlungsschnittstelle haben , die von einer Implementierung von BankPayment verwendet wird :

public interface Payment { void initiatePayments(); Object status(); List getPayments(); }

Und die Umsetzung:

public class BankPayment implements Payment { @Override public void initiatePayments() { // ... } @Override public Object status() { // ... } @Override public List getPayments() { // ... } }

Lassen Sie uns der Einfachheit halber die tatsächliche Geschäftsimplementierung dieser Methoden ignorieren.

Dies ist sehr klar - bisher benötigt die implementierende Klasse BankPayment alle Methoden in der Zahlungsschnittstelle . Somit verstößt es nicht gegen das Prinzip.

4. Verschmutzung der Schnittstelle

Jetzt, da wir rechtzeitig vorankommen und weitere Funktionen verfügbar sind , muss ein LoanPayment- Service hinzugefügt werden. Dieser Service ist auch eine Art Zahlung , hat aber einige weitere Operationen.

Um diese neue Funktion zu entwickeln, fügen wir der Zahlungsoberfläche die neuen Methoden hinzu :

public interface Payment { // original methods ... void intiateLoanSettlement(); void initiateRePayment(); }

Als nächstes haben wir die LoanPayment- Implementierung:

public class LoanPayment implements Payment { @Override public void initiatePayments() { throw new UnsupportedOperationException("This is not a bank payment"); } @Override public Object status() { // ... } @Override public List getPayments() { // ... } @Override public void intiateLoanSettlement() { // ... } @Override public void initiateRePayment() { // ... } }

Da sich nun die Zahlungsschnittstelle geändert hat und weitere Methoden hinzugefügt wurden, müssen alle implementierenden Klassen die neuen Methoden implementieren. Das Problem ist, dass die Implementierung unerwünscht ist und zu vielen Nebenwirkungen führen kann. Hier muss die LoanPayment- Implementierungsklasse die initiatePayments () implementieren, ohne dass dies tatsächlich erforderlich ist. Und so wird das Prinzip verletzt.

Was passiert also mit unserer BankPayment- Klasse:

public class BankPayment implements Payment { @Override public void initiatePayments() { // ... } @Override public Object status() { // ... } @Override public List getPayments() { // ... } @Override public void intiateLoanSettlement() { throw new UnsupportedOperationException("This is not a loan payment"); } @Override public void initiateRePayment() { throw new UnsupportedOperationException("This is not a loan payment"); } }

Beachten Sie, dass die BankPayment- Implementierung jetzt die neuen Methoden implementiert hat. Und da es sie nicht benötigt und keine Logik für sie hat, wird nur eine UnsupportedOperationException ausgelöst . Hier fangen wir an, gegen das Prinzip zu verstoßen.

Im nächsten Abschnitt werden wir sehen, wie wir dieses Problem lösen können.

5. Anwendung des Prinzips

Im letzten Abschnitt haben wir die Schnittstelle absichtlich verschmutzt und gegen das Prinzip verstoßen. In diesem Abschnitt erfahren Sie, wie Sie die neue Funktion für die Darlehenszahlung hinzufügen können, ohne gegen das Prinzip zu verstoßen.

Lassen Sie uns die Schnittstelle für jede Zahlungsart aufschlüsseln. Die aktuelle Situation:

Beachten Sie im Klassendiagramm und unter Bezugnahme auf die Schnittstellen im vorherigen Abschnitt, dass die Methoden status () und getPayments () in beiden Implementierungen erforderlich sind. Auf der anderen Seite ist initiatePayments () nur in BankPayment erforderlich , und die Methoden initiateLoanSettlement () und initiateRePayment () gelten nur für die LoanPayment .

Nachdem dies sortiert ist, teilen wir die Schnittstellen auf und wenden das Prinzip der Schnittstellentrennung an. Somit haben wir jetzt eine gemeinsame Schnittstelle:

public interface Payment { Object status(); List getPayments(); }

Und zwei weitere Schnittstellen für die beiden Zahlungsarten:

public interface Bank extends Payment { void initiatePayments(); }
public interface Loan extends Payment { void intiateLoanSettlement(); void initiateRePayment(); }

Und die jeweiligen Implementierungen, beginnend mit BankPayment :

public class BankPayment implements Bank { @Override public void initiatePayments() { // ... } @Override public Object status() { // ... } @Override public List getPayments() { // ... } }

Und schließlich unsere überarbeitete LoanPayment- Implementierung:

public class LoanPayment implements Loan { @Override public void intiateLoanSettlement() { // ... } @Override public void initiateRePayment() { // ... } @Override public Object status() { // ... } @Override public List getPayments() { // ... } }

Lassen Sie uns nun das neue Klassendiagramm überprüfen:

Wie wir sehen können, verstoßen die Schnittstellen nicht gegen das Prinzip. Die Implementierungen müssen keine leeren Methoden bereitstellen. Dies hält den Code sauber und verringert die Wahrscheinlichkeit von Fehlern.

6. Fazit

In diesem Tutorial haben wir uns ein einfaches Szenario angesehen, in dem wir zuerst vom Prinzip der Schnittstellentrennung abgewichen sind und die Probleme gesehen haben, die diese Abweichung verursacht hat. Dann haben wir gezeigt, wie man das Prinzip richtig anwendet, um diese Probleme zu vermeiden.

Wenn es sich um verschmutzte Legacy-Schnittstellen handelt, die wir nicht ändern können, kann das Adaptermuster nützlich sein.

Das Prinzip der Schnittstellentrennung ist ein wichtiges Konzept beim Entwerfen und Entwickeln von Anwendungen. Die Einhaltung dieses Prinzips hilft, aufgeblähte Schnittstellen mit mehreren Verantwortlichkeiten zu vermeiden. Dies hilft uns schließlich auch, das Prinzip der Einzelverantwortung zu befolgen.

Wie immer ist der Code auf GitHub verfügbar.