Entwurfsmuster (Design Patterns): Strukturmuster in Java
Thema:
Autor:
E-Mail:
Web:
Thema dieses Blog-Artikels sind die Strukturmuster (Structural Patterns), die als einer von drei Grundtypen innerhalb der Entwurfsmuster (Design Patterns) angesiedelt sind.
Entwurfsmuster (Design Patterns) sind in der Softwarearchitektur und -entwicklung allgemeine, wiederverwendbare und bewährte Lösungsmuster für wiederkehrende Entwurfsprobleme. Sie bilden somit eine wiederverwendbare Vorlage zur Problemlösung, welche in einem bestimmten Kontext eingesetzt werden kann. Es existieren drei unterschiedliche Grundtypen von Entwurfsmustern (Design Patterns): Erzeugungsmuster (Creational Patterns), Strukturmuster (Structural Patterns) und Verhaltensmuster (Behavioral Patterns).
Strukturmuster erleichtern durch die Herstellung von Beziehungen zwischen Klassen den Entwurf der Software.
Beispiele für Strukturmuster sind: Kompositum, Dekorierer, Brücke, Adapter, Proxy und Fliegengewicht.
Nachfolgend wird für zwei Strukturmuster (Dekorierer und Brücke) jeweils ein entsprechendes UML Diagramm dargestellt und erläutert sowie eine beispielhafte Implementierung aufgezeigt. Die Implementierung erfolgt in der objektorientierten Programmiersprache Java.
Dekorierer:
Das Entwurfsmuster „Dekorierer“ (decorator pattern) ist eine flexible Alternative zur Unterklassenbildung, um eine Klasse um zusätzliche Funktionalitäten zu erweitern.
Es wird eingesetzt, wenn
- einzelnen Objekten zusätzliche Funktionalität dynamisch und transparent hinzugefügt werden soll, ohne andere Objekte mit einzubeziehen,
- Funktionalität hinzugefügt werden soll, die auch wieder entfernt werden kann,
- die Erweiterung mittels Unterklassenbildung nicht praktisch durchführbar ist.
Das UML Diagramm für das Entwurfsmuster „Dekorierer“ stellt sich wie folgt dar:
UML Diagramm für das Entwurfsmuster "Dekorierer"
Die Akteure des Entwurfsmusters „Dekorierer“ sind wie folgt:
Component (Komponente):
Definiert eine Schnittstelle für Komponenten und Dekorierer.
ConcreteComponent (KonkreteKomponente):
Definiert ein Objekt, das um zusätzliche Funktionalitäten erweitert werden kann.
Decorator (Dekorierer):
Verwaltet eine Referenz auf ein Komponentenobjekt und definiert eine Schnittstelle, die der Schnittstelle von Komponente entspricht.
ConcreteDecorator (KonkreterDekorierer):
Fügt der Komponente Funktionalität hinzu.
Client (Klient):
Referenziert auf die Schnittstelle der Komponente.
Eine beispielhafte Java-Implementierung für das Entwurfsmuster „Dekorierer“ sieht wie folgt aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
/* * Design Pattern: Decorator Pattern (Dekorierer) * Modified code example based on the english wikipedia article: Decorator pattern * URL: https://en.wikipedia.org/wiki/Decorator_pattern * * Company: HyperCube IT Solutions * Author: Christian Paulus * Email: c.paulus@hypercube.biz * Web: www.hypercube.biz */ package design_patterns.structural_patterns; /* Client (Klient) */ class DecoratorPatternClient { public static void main(String args[]) { System.out.println("== Decorator Pattern Application =="); System.out.println(); // simple coffee Coffee simpleCoffee = new SimpleCoffee(); System.out.println("-> simple coffee"); System.out.println("cost: " + simpleCoffee.getCost()); System.out.println("ingredients: " + simpleCoffee.getIngredients()); System.out.println(); // coffee with milk Coffee coffeeWithMilk = new WithMilk(simpleCoffee); System.out.println("-> coffee with milk"); System.out.println("cost: " + coffeeWithMilk.getCost()); System.out.println("ingredients: " + coffeeWithMilk.getIngredients()); System.out.println(); // coffee with sugar Coffee coffeeWithSugar = new WithSugar(simpleCoffee); System.out.println("-> coffee with sugar"); System.out.println("cost: " + coffeeWithSugar.getCost()); System.out.println("ingredients: " + coffeeWithSugar.getIngredients()); System.out.println(); // coffee with milk and sugar Coffee coffeeWithMilkAndSugar = new WithSugar(new WithMilk(simpleCoffee)); System.out.println("-> coffee with milk and sugar"); System.out.println("cost: " + coffeeWithMilkAndSugar.getCost()); System.out.println("ingredients: " + coffeeWithMilkAndSugar.getIngredients()); System.out.println(); } } /* Component (Komponente) */ interface Coffee { public double getCost(); // operationA() public String getIngredients(); // operationB() } /* ConcreteComponent (KonkreteKomponente) */ class SimpleCoffee implements Coffee { public double getCost() { // operationA() return 1; } public String getIngredients() { // operationB() return "Coffee"; } } /* Decorator (Dekorierer) */ abstract class CoffeeDecorator implements Coffee { private Coffee coffee; // component: Component public CoffeeDecorator(Coffee coffee) { this.coffee = coffee; } public double getCost() { // operationA() return coffee.getCost(); } public String getIngredients() { // operationB() return coffee.getIngredients(); } } /* ConcreteDecoratorA (KonkreterDekoriererA) */ class WithMilk extends CoffeeDecorator { public WithMilk(Coffee coffee) { super(coffee); } @Override public double getCost() { // operationA() return super.getCost() + 0.5; } @Override public String getIngredients() { // operationB() return super.getIngredients() + ", Milk"; } } /* ConcreteDecoratorB (KonkreterDekoriererB) */ class WithSugar extends CoffeeDecorator { public WithSugar(Coffee coffee) { super(coffee); } @Override public double getCost() { // operationA() return super.getCost() + 0.2; } @Override public String getIngredients() { // operationB() return super.getIngredients() + ", Sugar"; } } |
Für die Übersichtlichkeit dieses Beispiels wurden alle Java-Klassen in einer Java-Datei aufgeführt, anstatt wie üblich jede Java-Klasse in einer eigenen Java-Datei aufzuführen.
Die Ausführung der Java-Datei DecoratorPattern.java in der Eclipse IDE liefert das folgende Ergebnis:
Ausführung von "DecoratorPattern.java" in Eclipse
Brücke
Das Entwurfsmuster „Brücke“ (bridge pattern) dient zur Trennung der Implementierung von ihrer Abstraktion (Schnittstelle), wodurch beide unabhängig voneinander verändert werden können.
Es wird eingesetzt, wenn
- sowohl Abstraktion als auch Implementierung erweiterbar sein sollen und eine dauerhafte Verbindung zwischen Abstraktion und Implementierung verhindert werden soll,
- Änderungen der Implementierung ohne Auswirkungen für den Klienten sein sollen,
- die Implementierung vor dem Klienten verborgen bleiben soll, oder
- die Implementierung von verschiedenen Klassen gleichzeitig genutzt werden soll.
Das UML Diagramm für das Entwurfsmuster „Brücke“ stellt sich wie folgt dar:
UML Diagramm für das Entwurfsmuster "Brücke"
Die Akteure des Entwurfsmusters „Brücke“ sind wie folgt:
Abstraction (Abstraktion):
Definiert die Schnittstelle der Abstraktion. Verwaltet eine Referenz auf ein Objekt des Typs „Implementor“.
RefinedAbstraktion (SpezAbstraktion):
Erweitert die durch Abstraktion definierte Schnittstelle.
Implementor (Implementierer):
Definiert die Schnittstelle für die Implementierungsklassen. Diese muss nicht notwendigerweise exakt mit der „Abstraction“-Schnittstelle übereinstimmen.
ConcreteImplementor (KonkreterImplementierer):
Implementiert die Schnittstelle „Implementor“ und definiert ihre konkrete Implementierung.
Client (Klient):
Referenziert auf die Schnittstelle der Abstraktion.
Eine beispielhafte Java-Implementierung für das Entwurfsmuster „Brücke“ sieht wie folgt aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 105 106 107 108 109 110 111 112 |
/* * Design Pattern: Bridge Pattern (Brücke) * Modified code example based on the german wikipedia article: Brücke (Entwurfsmuster) * URL: https://de.wikipedia.org/wiki/Brücke_(Entwurfsmuster) * * Company: HyperCube IT Solutions * Author: Christian Paulus * Email: c.paulus@hypercube.biz * Web: www.hypercube.biz */ package design_patterns.structural_patterns; /* Client (Klient) */ class BridgePatternClient { public static void main(String args[]) { System.out.println("== Bridge Pattern Application =="); System.out.println(); // Abstraction Printer printer = null; // ConcreteImplementorA & ConcreteImplementorB Printing plainTextPrinting = new PlainTextPrinting(); Printing htmlPrinting = new HTMLPrinting(); // RefinedAbstractionA + ConcreteImplementorA printer = new LaserPrinter(plainTextPrinting); printer.print(); // operation() + operationImplementation() // RefinedAbstractionA + ConcreteImplementorB printer.setPrinting(htmlPrinting); printer.print(); // operation() + operationImplementation() System.out.println(); // RefinedAbstractionB + ConcreteImplementorA printer = new InkjetPrinter(plainTextPrinting); printer.print(); // operation() + operationImplementation() // RefinedAbstractionB + ConcreteImplementorB printer.setPrinting(htmlPrinting); printer.print(); // operation() + operationImplementation() } } /* Abstraction (Abstraktion) */ abstract class Printer { protected Printing printing; // implementor: Implementor public Printer(Printing printing) { this.printing = printing; } public abstract void print(); // operation() public Printing getPrinting() { return this.printing; } public void setPrinting(Printing printing) { this.printing = printing; } } /* RefinedAbstractionA (SpezialisierteAbstraktionA) */ class LaserPrinter extends Printer { public LaserPrinter(Printing printing) { super(printing); } @Override public void print() { // operation() printing.print("Printed with LASER PRINTER."); } } /* RefinedAbstractionB (SpezialisierteAbstraktionB) */ class InkjetPrinter extends Printer { public InkjetPrinter(Printing printing) { super(printing); } @Override public void print() { // operation() printing.print("Printed with INKJET PRINTER."); } } /* Implementor (Implementierer) */ interface Printing { public void print(String text); // operationImplementation() } /* ConcreteImplementorA (KonkreterImplementiererA) */ class PlainTextPrinting implements Printing { public void print(String text) { // operationImplementation() System.out.println(text); } } /* ConcreteImplementorB (KonkreterImplementiererB) */ class HTMLPrinting implements Printing { public void print(String text) { // operationImplementation() System.out.println("<p>\n\t<em>" + text + "</em>\n</p>"); } } |
Für die Übersichtlichkeit dieses Beispiels wurden alle Java-Klassen in einer Java-Datei aufgeführt, anstatt wie üblich jede Java-Klasse in einer eigenen Java-Datei aufzuführen.
Die Ausführung der Java-Datei BridgePattern.java in der Eclipse IDE liefert das folgende Ergebnis:
Ausführung von "BridgePattern.java" in Eclipse
Quellenangaben:
- Gamma, Erich et al. – Design Patterns: Elements of Reusable Object-Oriented Software; Addison-Wesley, 2009
- Gamma, Erich et al. – Entwurfsmuster: Elemente wiederverwendbarer objektorientierter Software; Addison-Wesley, 2011
- Gamma, Erich et al. – Design Patterns: Entwurfsmuster als Elemente wiederverwendbarer objektorientierter Software; mitp, 2015
- Wikipedia – Decorator pattern: https://en.wikipedia.org/wiki/Decorator_pattern
- Wikipedia – Decorator: https://de.wikipedia.org/wiki/Decorator
- Wikipedia – Bridge pattern: https://en.wikipedia.org/wiki/Bridge_pattern
- Wikipedia – Brücke (Entwurfsmuster): https://de.wikipedia.org/wiki/Brücke_(Entwurfsmuster)