JAVA Interfaces! Warum, weshalb und wieso?

Keylearnings:

  • Warum du Java Interfaces verwenden solltest.
  • Was ist Interface Polymorphie?
  • Was ist setter- Konstruktor Dependency Injection?

Du musst nicht wissen wie der Strom in die Steckdose kommt.

Auch nicht wie er da wieder herauskommt. Außer du bist Elektriker.

Es reicht völlig, dass du weißt wie du den Stecker deines Computers, Fernsehers oder Föhns in die Dose stöpselst.

Alles worauf es ankommt ist, dass der Stecker in die Dose passt!

Genau dieses Konzept können wir mit Hilfe von Interfaces auch in unseren Java Programmen verwenden.

In der Programmierwelt sprechen wir hierbei vom Prinzip des Least Knowledge oder auf deutsch das Prinzip vom wenigen Wissen.

In diesem Artikel möchte ich dir die Verwendung von Java Interfaces anhand des Beispiels der Zahlungsmöglichkeiten in einem Webshop erklären und die Frage warum du Interfaces verwenden solltest beantworten.

Hierbei werden wir auch über Konzepte wie Interface Polymorphie und Dependency Injection sprechen.

Setze auf Interfaces!

Ich predige es auf meinem Blog immer und immer wieder.

Verwende Interfaces!

Doch warum eigentlich?

Auf den ersten Blick erscheint dieses Konzept höchst langweilig zu sein. Schließlich können wir in einem Java Interface lediglich statische Variablen und Methodensignaturen definieren.

Das coole hierbei ist jedoch, dass wir mit Hilfe von Interfaces die Definition und die eigentliche Implementierung einer Funktionalität voneinander trennen können.

Wodurch wir die Möglichkeit haben eine Funktionalität zu verwenden ohne uns mit Implementierungs-Details beschäftigen zu müssen.

Die einzige Information, die wir benötigen ist, welchen Input braucht eine Funktionalität und welchen Output erhalten wir zurück.

Und genau das ist es worum es beim Prinzip des Least Knowledge geht.

Java Interfaces bieten uns also die Möglichkeit die beiden Fragen „Was wir tun wollen?“ und „Wie wir es tun?“ voneinander zu trennen.

Herrje, und was bringt uns das nun schon wieder?

Schauen wir uns das am praktischen Beispiel eines Webshops an.

Design Prinzip Java Interfaces

Damit der Webshop Umsatz generiert, muss der Anwender die Möglichkeit haben Rechnungen zu bezahlen.

Hierfür stehen unterschiedliche Zahlungsmethoden zur Auswahl. Der Kunde kann mit Kreditkarte, per EC-Karte oder mittels des Zahlungsanbieters PayPal bezahlen.

Wie sieht die Implementierung des Webshops ohne die Verwendung von Interfaces aus?

Wir erstellen eine Klasse mit dem Namen Webshop, die für jede Zahlungsart eine Methode zur Verfügung stellt.

1: public class Webshop {
	
2:	private String name = null;
	
3:	public Webshop(String name) {
4:	  this.name = name;
5:	}

6:	public void zahlePerEC(){
7:	  System.out.println("Zahlungslogik EC");
8:	}
	
9:	public void zahlePerPayPal(){
10:	  System.out.println("Zahlungslogik PayPal");
11:	}
	
12:	public void zahlePerKreditkarte(){
13:	  System.out.println("Zahlungslogik Kreditkarte");
14:	}
		
}

Neben einem Attribut name, das mit Hilfe des Konstruktor initialisiert werden und den Namen des Webshops enthält, besteht die Klasse aus den drei Methoden zahlePerEC,zahlePerPayPal und zahlePerKreditkarte, welche Dummy-Implementierungen der verschiedenen Zahlungsarten enthalten.

Doch welchen Nachteil hat diese Vorgehensweise?

Was machen wir, wenn beispielsweise PayPal beschließt ihre Implementierungslogik zu ändern?

Japp, dann haben wir richtig Stress!

Wir müssten dann nämlich die Implementierung der zahlePerPayPal Methode innerhalb unseres Webshops anpassen.

Und um genau das zu vermeiden können wir Java Interfaces verwenden.

Interfaces ermöglichen es uns die Implementierung der Zahlungsarten aus der Klasse Webshop zu entfernen und uns hier lediglich auf die eigentliche Aktion, nämlich die Zahlung, zu konzentrieren.

Wie die Zahlung letztlich erfolgt (PayPal, Kreditkarte etc.) spielt dann für die Webshop Klasse keine Rolle mehr.

Okay, gehen wir es an. Wir definieren ein Java Interface IZahlung.

public interface IZahlung {
	
	public void erzeugeZahlung();
	
}

Das Interface enthält nur eine Methodensignatur erzeugeZahlung.

Um diese Methode zu implementieren, d.h. mit Funktion zu füllen, müssen wir es in eine Klasse einbinden.

Da wir in unserem Webshop insgesamt drei verschiedene Zahlungsarten anbieten, erstellen wir für jede Art eine eigene Klasse, die das Interface IZahlung implementiert.

Alle drei Klassen sind Strukturgleich.

Beginnen wir mit der Klasse, welche die PayPal Funktionalität implementiert.

1: public class PayPal implements IZahlung {

	@Override
2:	public void erzeugeZahlung() {
3:		System.out.println("Zahlungslogik PayPal");	
4:	}

5:}

Mit Hilfe des Schlüsselwortes implements binden wir in Zeile eins zunächst das Java Interface IZahlung in die Klasse ein.

Hierdurch werden wir vom Compiler gezwungen die im Interface IZahlung definierte Methode erzeugeZahlung, welche die PayPal Zahlungslogik beinhaltet zu überschreiben.

Nahezu identisch sehen die Klassen für die Kredit- und EC-Kartenzahlung aus.

Hier die Klasse für die EC-Kartenzahlung:

public class EC implements IZahlung{

	@Override
	public void erzeugeZahlung() {
		System.out.println("Zahlungslogik EC");
		
	}
}

Und fast ohne Unterschied die Klasse für die Kredikartenzahlung:

public class Kreditkarte implements IZahlung{

	@Override
	public void erzeugeZahlung() {
		System.out.println("Zahlungslogik Kreditkarte");
		
	}	
}

Unsere drei Klassen unterscheiden sich also lediglich in der Art wie die Methode erzeugeZahlung definiert ist.

Das nächste Ziel ist es, die erzeugeZahlung Methode in unserem Webshop zu verwenden.

Interfaces und Polymorphie

Um die verschiedenen Zahlungsarten in unserem Webshop verwenden zu können, benutzen wir das Konzept mit dem coolsten Namen überhaupt.

Das Konzept der Interface Polymorphie

Klingt zwar kompliziert ist aber eigentlich total entspannt.

Interface Polymorphie bedeutet, dass du eine Variable mit dem Namen deines Java Interfaces definieren kannst und eine Instanz von einer Klasse, die das Interface implementiert, dieser Variablen zuweisen kannst.

Über diese Instanz können wir anschließend alle im Interface definierten Methoden aufrufen.

Aber jetzt Schluss mit der Theorie schauen wir uns das ganze praktisch an unserem Webshop Beispiel an.

Wir haben ein Interface mit dem Namen IZahlung definiert. Daher legen wir eine Variable zahlungsart vom Typ IZahlung an.

private IZahlung zahlungsart = null;

Nach dem Anlegen der Variable zahlungsart hat diese jedoch den Wert null.

Daherhaben wir als nächstes zu klären, wie wir der Variable zahlungsart eine Instanz zuweisen.

Und wie du vielleicht schon richtig vermutest, machen wir das mit Hilfe eines Konstruktors und einer setter-Methode. Aber auch das hat wieder einen coolen Namen.

Setter- und Konstruktor Dependency Injection

Um dem Attribut zahlungsart beim Erzeugen der Webshop Instanz einen Wert zuweisen zu können, erweitern wir im ersten Schritt den Konstruktor um einen weiteren Parameter vom Typ unseres Java Interfaces IZahlung.

public Webshop(String name,IZahlung iz) {
	this.name = name;
	this.zahlungsart = iz;
}

Über den Parameter iz können wir jetzt die IZahlungsart Instanz bei Erstellung unseres Webshops initialisieren. Dieses Vorgehen nennen wir Konstruktor-Injection.

Des Weiteren erstellen wir eine setter-Methode, mit der wir die Zahlart auch noch nach Erzeugung der Webshop Instanz verändern können.

public void setZahlungsart(IZahlung zahlungsart) {
	this.zahlungsart = zahlungsart;
}

Die setter-Methode erwartet als Parameter eine Instanz vom Typ IZahlung, die wir dem Webshop-Attribut zahlungsart zuweisen.

Jetzt fehlt nur noch eines!

Wir müssen noch die Möglichkeit schaffen die Interface-Methode erzeugeZahlung aufzurufen.

Hierzu erstellen wir eine Methode zahlen(), die nichts anderes macht als die erzeugeZahlung Methode der zahlungsart Instanz aufzurufen.

public void zahlen(){
   zahlungsart.erzeugeZahlung();
}	

Und erkennst du es?

Diese Methode ist völlig unabhängig von einer konkreten Zahlungsart Implementierung, ob nun mit PayPal, Kredit- oder EC-Karte bezahlt wird hängt nur davon ab, ob wir der zahlungsart Instanz ein PayPal-, EC- oder Kreditkarten-Objekt über den Konstruktor bzw. die setter-Methode zugewiesen haben.

Wir haben also unser Ziel erreicht und die Logik der Zahlungsanbieter vollständig aus der Webshop-Klasse in externe Klassen verlagert.

Innerhalb der Webshop-Klasse steht jetzt nur noch die Aktion, nämlich die Zahlung, im Fokus. Auf welche Weise diese Zahlung erfolgt ist Aufgabe der Klassen, PayPal, EC und Kreditkarte.

Ein weiteres Beispiel zur Dependency Injection findest du im folgenden Video.

Testen wir unser Programm!

Okay, kommen wir zur Stunde der Wahrheit und testen unser Programm in dem wir unseren Webshop mit den unterschiedlichen Zahlarten betreiben.

1.) Wir erzeugen eine Instanz des Webshops und initialisieren die Zahlart mit der PayPal Zahlungsmethode.

Webshop w = new Webshop("Shop 1",new PayPal());

Lassen wir uns das einen Moment auf der Zunge zergehen und insbesondere einen Blick auf die Argumente des Konstruktors werfen.

Der erste Parameter ist noch unspektakulär. Hier setzen wir lediglich das Namensattribut des Webshops auf den Namen Shop 1.

Spannend ist der zweite Parameter. Hier übergeben wir eine neu erzeugte Instanz der PayPal Klasse und sehen den Interface Polymorphismus in Action.

Der zweite Parameter ist nämlich vom Typ unseres Java Interfaces IZahlung und weil die Klasse PayPal dieses Interface implementiert ist der Typ IZahlung mit dem Typ PayPal kompatibel.

Als nächstes rufen wir die Methode zahlen() der Webshop Instanz w auf.

w.zahlen();

Da wir der Variable zahlungsart eine PayPal Instanz zugewiesen haben, führt dies zu einem Aufruf der erzeugeZahlung Implementierung aus der Klasse PayPal.

Aus diesem Grund erhalten wir die Bildschirmausgabe:

Zahlungslogik PayPal

Als nächstes wollen wir mit Hilfe der setter-Methode setZahlungsart die Zahlungsart von PayPal auf Kreditkarten-Zahlung ändern.

Hierzu müssen wir der setter-Methode lediglich eine Kreditkarten-Instanz als Parameter übergeben.

w.setZahlungsart(new Kreditkarte());

Rufen wir jetzt die zahlen() Methode der Webshop-Klasse auf, erhalten wir wie erwartet die Ausgabe:

Zahlungslogik Kreditkarte

Mit Hilfe der setter Methode setZahlungsart können wir also dynamisch während der Laufzeit des Programms die Verarbeitungsart der Zahlung ändern.

Dies entspricht einem bekannten objektorientierten Design Muster. Nämlich dem sogenannten Strategy Pattern.

Fazit: In diesem Artikel haben wir uns angesehen wie wir die Implementierung und die Definition einer Funktionalität mit Hilfe von Java Interfaces trennen können. Mit Hilfe von Interface Polymorphie und Dependency Injection haben wir außerdem gesehen wie wir während der Laufzeit die Implementierung einer Funktionalität austauschen können.

Hat dir der Artikel gefallen? Dann folge uns doch gleich auf Facebook!

Hallo ich bin Kim und ich möchte ein großer Programmierer werden. Machst du mit?

Kommentare (0)

Hinterlasse ein Kommentar