Facade Pattern: Einheitliche Schnittstellen für Software-Projekte

Auf der Suche nach geeigneten Strategien zur Vereinfachung komplexer Software trifft man unweigerlich auf das Facade Design Pattern (dt. Fassaden-Entwurfsmuster) oder kurz einfach nur Facade Pattern. Neben anderen Vertretern wie dem Decorator Pattern oder dem Composite Pattern zählt es zur Kategorie der Strukturmuster der sogenannten GoF-Entwurfsmuster (die Abkürzung „GoF“ steht für „Gang of Four“), die das Software-Design seit ihrer Veröffentlichung im Jahr 1994 entscheidend prägen.

Was genau das Facade Pattern ist und in welcher Hinsicht es Entwicklern bei der Entzerrung von Subsystemen weiterhilft, erfahren Sie in den nachfolgenden Abschnitten.

Was ist das Facade Pattern (Facade-Entwurfsmuster)?

Das Facade Design Pattern ist eines der 23 sogenannten GoF-Design-Patterns, die 1994 als Leitfäden für die Software-Entwicklung von den vier Autoren Erich Gamma, Ralph Johnson, Richard Helm und John Vlissides in „Design Patterns: Elements of Reusable Object-Oriented Software“ veröffentlich wurden. Dem allgemeinen Grundsatz dieser Patterns folgend, die Kreation flexibler, wiederverwendbarer Software zu vereinfachen, definiert das Facade Pattern eine Musterlösung zur einfachen Zusammenführung verschiedener Schnittstellen in komplexen Systemen. Eine universelle Fassaden-Klasse, die gleichzeitig als Schnittstelle fungiert, delegiert dabei wichtige Funktionalitäten der Software an die jeweiligen Subsysteme, um den Umgang mit den verschiedenen Teilkomponenten eines Programms so einfach wie möglich zu gestalten.

Welche Probleme löst der Facade-Pattern-Ansatz?

Clients, die auf ein komplexes Subsystem zugreifen, beziehen sich direkt auf eine Vielzahl von Objekten mit ganz verschiedenen Schnittstellen oder sind von diesen Objekten abhängig. Das macht die Implementierung, Anpassung sowie das Testen und die Wiederverwendung der Clients aus Entwicklersicht besonders schwierig – und bringt das Facade Design Pattern ins Spiel:

Das Facade-Entwurfsmuster sieht die Definition eines zentralen Facade-Objekts (auch als „Fassade“ bezeichnet) vor, das:

  • ein universelles Interface für die verschiedenen Schnittstellen des Subsystems bzw. der Subsysteme implementiert.
  • und (bei Bedarf) zusätzliche Funktionen vor oder nach der Weiterleitung einer Client-Anfrage ausführen kann.

Als Vermittler sorgt das Facade-Objekt dafür, dass der Zugriff bzw. die Kommunikation mit den einzelnen Komponenten eines Subsystems vereinfacht und damit auch die direkte Abhängigkeit von diesen Komponenten minimiert wird. Es delegiert die Clientaufrufe, sodass Clients weder die Klassen noch ihre Beziehungen und Abhängigkeiten kennen müssen.

Zur Anzeige dieses Videos sind Cookies von Drittanbietern erforderlich. Ihre Cookie-Einstellungen können Sie hier aufrufen und ändern.

Facade Pattern: UML-Klassendiagramm des Fassaden-Musters

Die Fassade bzw. die Facade-Klasse ist die entscheidende Strukturierungseinheit des Facade Patterns. Ihre Implementierung und Ausarbeitung ist also die grundlegende Aufgabe für Entwickler, die ihre komplexe Software mithilfe dieses praktischen Entwurfsmusters vereinfachen möchten. Einmal umgesetzt, konzentrieren die betroffenen Client-Objekte ihre gesamte Kommunikation auf die Facade-Klasse, die damit in diesem neuen System zur einzigen Instanz wird, von der die Clients direkt abhängig sind.

Das nachfolgende UML-Diagramm stellt das Zusammenspiel von Clients, Fassade und Subsystem-Klassen nach dem Facade-Pattern zur Veranschaulichung grafisch dar.

Facade Pattern: Vorteile und Nachteile

Die Stärken des Facade Design Patterns liegen auf der Hand: Die Fassade „versteckt“ zugrundeliegende Subsysteme einer Software und senkt dadurch die Komplexität dieser Systeme. Zusätzlich fördert dieser Ansatz das Prinzip der losen Kopplung. Durch den geringen Grad der Abhängigkeit der einzelnen Komponenten untereinander sind Änderungen (Modifizierungen, Wartungen) jederzeit ohne größere Umstände möglich, da die Änderungen sich zum Großteil nur lokal auswirken. Diese lose Kopplung sorgt außerdem dafür, dass ein Subsystem einfacher zu erweitern ist.

Hinweis

Sollten Clients auf einen direkten Zugriff auf bestimmte Klassen des Subsystems angewiesen sein, kann dieser auch im Facade-Pattern-Modell gewährt werden: In diesem Fall ist lediglich die Sichtbarkeit des Subsystems so zu programmieren, dass ein Client die Fassade bei Bedarf übergehen kann.

Der Einsatz des Facade-Entwurfsmusters kann jedoch auch entscheidende Nachteile mit sich bringen: Die Implementierung einer Fassade ist – durch ihre zentrale Rolle – eine sehr mühsame und komplizierte Aufgabe, insbesondere, wenn sie in bereits bestehenden Code eingefügt werden soll. Generell bedeutet der Einbau eines Facade-Interfaces eine zusätzliche Indirektionsstufe, die an zusätzliche Rechenzeit für Methoden- und Funktionsaufrufe, Speicherzugriffe etc. geknüpft ist. Schließlich birgt das Facade Pattern auch das Risiko, dass die Abhängigkeit der Software von der zentralen Ober-Schnittstelle zu groß wird.

Vorteile Nachteile
Minimiert die Komplexität von Subsystemen Implementierung aufwändig (insbesondere bei bereits bestehendem Code)
Fördert das Prinzip der losen Kopplung Ansatz ist an eine zusätzliche Indirektionsstufe gekoppelt
Software wird flexibler und leichter erweiterbar Hoher Abhängigkeitsgrad von der Facade-Schnittstelle

Typische Anwendungsfälle für das Facade Design Pattern

Die Eigenschaften des Facade-Entwurfsmusters machen es gleich für mehrere Einsatzszenarios interessant. Allen voran steht der Wunsch nach einer einheitlichen Schnittstelle für den Zugriff auf komplexe Subsysteme oder eine beliebige Menge an Objekten. Hier verspricht eine Fassade eine deutliche Vereinfachung, weshalb der Einsatz der Facade-Pattern-Strategie bei der Planung des Projekts eine übergeordnete Rolle spielen sollte.

Ein weiterer typischer Anwendungsfall ist Software, bei der die Abhängigkeit zwischen Clients und zugrundeliegenden Subsystemen minimiert werden soll.

Zuletzt zahlt sich der Facade-Pattern-Ansatz aus, wenn Sie ein Software-Projekt planen, das in mehrere Schichten unterteilt werden soll. Fassaden als Kommunikationsschnittstellen zwischen den Schichten sorgen auch dabei für mehr Flexibilität bei der späteren Erweiterung und Anpassung der Komponenten.

Praxis-Beispiel für die Umsetzung des Facade Patterns

Das Facade Design Pattern ist als Entwurfsmuster nicht an eine bestimmte Programmiersprache gebunden. Unter anderem kommt die Strategie beispielsweise in C++, C#, JavaScript, Java, PHP und Python zum Einsatz. Im nachfolgenden Facade-Pattern-Beispiel, bei dem wir uns an dem Facade-Pattern-Tutorial auf tutorialspoint orientiert haben, handelt es sich daher nur exemplarisch um einen Java-Code.

Im Beispiel soll ein universell geltendes Interface „Shape“ für Objekte definiert werden, die geometrische Formen repräsentieren. Zudem werden konkrete Klassen generiert, die dieses Interface implementieren, sowie eine Facade-Klasse namens „ShapeMaker“, die für das Delegieren der Client-Anfragen verantwortlich ist.

Zunächst erstellen wir das Interface Shape.java mit folgendem Code:

public interface Shape {
	void draw();
}

Im zweiten Schritt werden mit Rectangle.java (Klasse für rechteckige Objekte), Square.java (Klasse für quadratische Objekte) und Circle.java (Klasse für kreisförmige Objekte) drei konkrete Klassen erzeugt, die das Interface implementieren.

public class Rectangle implements Shape {
	@Override
	public void draw() {
		System.out.println("Rectangle::draw()");
	}
}
public class Square implements Shape {
	@Override
	public void draw() {
		System.out.println("Rectangle::draw()");
	}
}
public class Circle implements Shape {
	@Override
	public void draw() {
		System.out.println("Rectangle::draw()");
	}
}

Schließlich wird die Facade-Klasse ShapeMaker in den Code integriert, die fortan von den Clients angesprochen werden kann, um die verschiedenen Formen zu erzeugen:

public class ShapeMaker {
	private Shape circle;
	private Shape rectangle;
	private Shape square;
	public ShapeMaker() {
		circle = new Circle();
		rectangle = new Rectangle();
		square = new Square();
	}
	public void drawCircle(){
		circle.draw();
	}
	public void drawRectangle(){
		rectangle.draw();
	}
	public void drawSquare(){
		square.draw();
	}
}