design-patterns-facade.html 9.03 KB
Newer Older
1
2
3
4
5
<p>Wie der Adapter ist auch die Fassade (engl. <i>Facade</i>) einer Hüllenklasse. Während der Adapter hilft Methoden aus externen Komponenten aufzurufen und diese in das eigene System zu integrieren, ist es das Ziel der Fassade das eigene System nach außen so zu gestalten, dass es einfach durch externe Komponenten (= <i>Clients</i>) aufzurufen ist. Die Fassade gestaltet also die Schnittstelle über die Zugriffe von außen erfolgen. Es sollen i.d.R. nicht alle Methoden mit ihrer internen Signatur nach außen zur Verfügung gestellt werden. Die folgende Abbildung skizziert die Idee einer Fassade in einem UML-Komponentendiagramm.</p>

<img src="media/patterns_facade.png" style="width:500px">
<label>Entwurfsmuster Fassade</label>

Jens Ehlers's avatar
Jens Ehlers committed
6
<p>Eine Fassade entkoppelt die Komponenten des entwickelten Systems von seinen Clients. Die Clients dürfen ausschließlich über die Fassade auf das System zugreifen. Hinter der Fassade gibt es mehrere Komponenten oder Subsysteme, die ihrerseits definierte Schnittstellen aufweisen und als Bausteine das System selbst verkörpern. Die Komponenten sind häufig viel komplexer, als es ein Client benötigt. Sie können in unterschiedlichen Programmiersprachen, mittels verschiedener Frameworks realisiert sein und in heterogenen Laufzeitumgebungen bereitgestellt werden (s. Kapitel <a href="#unit-architecture">Modularisierung und Architektur</a>). Die Fassade greift ihrerseits auf diese Komponenten zu und verbirgt deren Komplexität vor den Clients. Eine Methode der Fassade kann z.B. eine logische Folge von internen Methodenaufrufen zu einer höherwertigen Funktionalität zusammenführen.</p>
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<p>In der Praxis sind Fassaden-Klassen sehr häufig vorzufinden, da nahezu jedes System über die Jahre an Komplexität gewinnt und so irgendwann der Bedarf nach einer Fassade entsteht, um diese interne Komplexität überschaubar zu gestalten. Ein Softwaresystem entwickelt sich i.d.R. organisch weiter, dadurch dass verschiedene Entwickler an verschiedenen Stellen neue Klassen, Beziehungen und Methoden hinzufügen. Das System wird im Laufe seiner kontinuierlichen Weiterentwicklung sukzessive in Komponenten zerlegt, die von unterschiedlichen Teams betreut werden.</p>

<p>Fassaden können auch helfen, wenn später mehrere Komponenten wieder integriert werden sollen, weil sie fachlich ähnliche Funktionalitäten beinhalten. Die Fassade bietet dann eine einheitliche Schnittstelle über die Zugriffe von außen an die existierenden Komponenten delegiert werden können, während diese hinter der Fassade langsam verschmelzen.</p>

<p>Es gibt also zusammenfassend verschiedene Motive eine Fassade einzusetzen:
<ul>
<li>Ein Client soll mit einer einfachen Schnittstelle kommunizieren. Die Komplexität hinter der Fassade wird abstrahiert, so dass sich der Client damit nicht auseinandersetzen muss.</li>
<li>Hinter einer Fassade können die Komponenten grundsätzlich weiterentwickelt werden (insbesondere Refactoring), ohne dass ein Client dies bemerkt. Die Schnittstelle der Fassade soll dabei möglichst stabil bleiben, während ihre Implementierung sich an die Änderungen in den internen Komponenten anpasst.</li>
<li>Allgemein unterstützt eine Fassade das Prinzip loser Kopplung zwischen Komponenten, um deren Abhängigkeiten voneinander zu verringern. Die lose Kopplung erleichtert die Portierung und die Replikation einzelner Komponenten in einem verteilten System. Portierung heißt, dass eine Komponente unter eine anderen Adresse erreichbar gemacht wird, z.B. weil sich der Host von einem dedizierten Server im eigenen Rechenzentrum in Richtung eines virtuellen Servers bei einem Cloud-Dienstleister ändert. Replikation heißt, dass eine Komponente im verteilten System mehrfach instantiiert wird, um ggf. eine höhere Systemlast tragen zu können.</li>
<li>Eine Fassade kann auch der Sicherheit dienen. Das System ist einfacher zu schützen, wenn Zugriffe nur über die Fassade erfolgen. Die Clients werden durch die Fassade ihrerseits vor fehlerhafter Anwendung geschützt, d.h. vor andernfalls möglichen Kombinationen und Parametrisierungen von Methodenaufrufen, die fachlich nicht sinnvoll sind.</li>
<li>Querschnittliche Belange (engl. <i>Cross-cutting Concerns</i>) können von einer Fassade übernommen werden, z.B. Authentifizierung und Autorisierung, Session-Verwaltung, allgemeine Validierungen, Logging oder Monitoring.</li>
</ul></p>

21
<p>Das in der obigen Abbildung dargestellte UML-Komponentendiagramm soll anhand der mit Java 9 eingeführten <a href="https://www.informatik-aktuell.de/entwicklung/programmiersprachen/java-9-das-neue-modulsystem-jigsaw-tutorial.html">Module</a> (= Komponenten) folgend implementiert werden. Die Programmiersprache Java bietet seit ihrer Entstehung die Möglichkeit, die Sichtbarkeit von Klassen, ihren Attributen und Methoden, usw. einzuschränken. Per Default ist die Sichtbarkeit auf Inhalte innerhalb des eigenen Package beschränkt. Der Default kann über die bekannten Modifizierer <code>public</code>, <code>private</code> und <code>protected</code> angepasst werden. Seit Java 9 gibt es auf höherer Abstraktionsebene nun Module, die den Komponenten der Architektur entsprechen und über die ebenfalls die Sichtbarkeit gesteuert werden kann. Diese Module umfassen i.d.R. mehrere Packages und spezifizieren in ihrem Modul-Deskriptor (<code>module-info.java</code>) zum einen, von welchen anderen Modulen sie abhängig sind (<code>requires &lt;&lt;module&gt;&gt;</code>), und zum anderen, welche ihrer Packages sie an andere Module nach außen freigeben (<code>exports &lt;&lt;package&gt;&gt;</code>). Per Konvention werden die Module in Java genau wie Packages kleingeschrieben und mit einer umgekehrten Domain als identifizierendes Prefix bezeichnet. Auf das Domain-Prefix wird hier zur Vereinfachung verzichtet.</p>
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104

<ul class="nav nav-tabs" id="facade-tabs" role="tablist">
  <li class="nav-item"><a href="#facade-tabs-client" class="nav-link active" data-toggle="tab" role="tab">Modul Client</a></li>
  <li class="nav-item"><a href="#facade-tabs-facade" class="nav-link" data-toggle="tab" role="tab">Modul MySystem</a></li>
  <li class="nav-item"><a href="#facade-tabs-componentX" class="nav-link" data-toggle="tab" role="tab">Modul ComponentX</a></li>
  <li class="nav-item"><a href="#facade-tabs-componentY" class="nav-link" data-toggle="tab" role="tab">Modul ComponentY</a></li>
</ul>
<div class="tab-content" id="facade-tabs-content">
  <div class="tab-pane show active" id="facade-tabs-client" role="tabpanel">
	<pre><code class="language-java line-numbers">// module-info.java
module client {
    requires mySystem;
}

// Client.java
package client;
import facade.Facade;

public class Client {
    public static void main(String[] args) {
        System.out.println(Facade.helloFromX());
        System.out.println(Facade.welcomeHelloFromY());
        System.out.println(Facade.welcomeHelloFromXY());
    }
}</code></pre> 
  </div>
  <div class="tab-pane" id="facade-tabs-facade" role="tabpanel">
	<pre><code class="language-java line-numbers">// module-info.java
module mySystem {
    requires componentX;
    requires componentY;
    exports facade;
}

// Facade.java
package facade;
import x.X2;
import y.Y1;

public class Facade {
    public static String helloFromX() { return X2.hello("X2"); }
    public static String welcomeHelloFromY() { return Y1.welcome() + Y1.hello(); }
    public static String welcomeHelloFromXY() { return Y1.welcome() + X2.hello("X2") + Y1.hello(); }
}</code></pre> 
  </div>
  <div class="tab-pane" id="facade-tabs-componentX" role="tabpanel">
	<pre><code class="language-java line-numbers">// module-info.java
module componentX {
    exports x to mySystem;
}

// X2.java
package x;

public class X2 extends X1 {
    public static String hello(String from) { return "Hello from " + from + ". "; }
    X3[] x3;
}</code></pre> 
  </div>
  <div class="tab-pane" id="facade-tabs-componentY" role="tabpanel">
	<pre><code class="language-java line-numbers">// module-info.java
module componentY {
    exports y to mySystem;
}

// Y1.java
package y;

public class Y1 {
    public static String welcome() { return "Welcome! "; }
    public static String hello() { return "Hello from " + Y1.class.getSimpleName() + ". "; }
}</code></pre> 
  </div>  
</div>

<p><ul>
<li>Das Modul <code>client</code> kann hier ausschließlich über das Modul <code>mySystem</code> auf die als Bausteine enthaltenen Komponenten zugreifen.</li>
<li>Die Inhalte der Packages in den Modulen <code>componentX</code> und <code>componentY</code> kann der Client nicht importieren, das diese nur für die Fassade freigegeben sind (<code>exports x to mySystem</code>).</li>
<li>Die Fassade hingegen exportiert die Inhalte ihres Package öffentlich an beliebige interessierte Clients (<code>exports facade</code>).</li>
<li>In der Klasse <code>Facade</code> ist zu erkennen, das in den Methoden der Fassade durchaus mehrere Methoden aus den Komponenten kombiniert werden können und deren Parametrisierung eingeschränkt werden kann.</li>
</ul></p>

<p>Die gezeigten Code-Beispiele zum Entwurfsmuster Fassade finden sich im Verzeichnis <a href="/patterns/facade" class="repo-link">/patterns/facade</a> des Modul-Repository.</p>