Start
Unternehmen
ERP / PPS / Prozesse
Business Intelligence
Server-Technologien
Software-Technologien
Technologie-Beratung
Individual-Software
Produkte

Comelio GmbH
Rellinghauser Straße 10
D-45128 Essen
Deutschland
Fon: 0201-437517-0
Fax: 0201-437517-10
info@comelio.com

Comelio GmbH
Goethestraße 34
D-13086 Berlin
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Glockengießerwall 17
D-20095 Hamburg
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Mainzer Landstraße 27-31
D-60329 Frankfurt
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Stiglmaierplatz/Dachauer Str. 37
D-80335 München
Deutschland
info@comelio.com

Comelio GmbH (Ecos)
Liebknechtstr. 33
D-70565 Stuttgart
Deutschland
info@comelio.com

Comelio GmbH
Nevinghoff 16
D-48147 Münster
Deutschland

Comelio GmbH
Friedrich - List - Platz 1
D-04103 Leipzig
Deutschland

Comelio GmbH
St. Johanner Strasse 41-43
D-66111 Saarbrücken
Deutschland

Comelio GmbH
Kaiser-Wilhem-Ring 27–29
D-50672 Köln
Deutschland

Comelio GmbH
Münsterstraße 248
D-40470 Düsseldorf
Deutschland

Comelio GmbH
Fürther Strasse
D-90429 Nürnberg
Deutschland

Comelio GmbH

Bremen
Deutschland

Comelio-Blog > C#.NET > Dekorierer-Muster

Entwurfsmuster (Design Patterns) in C#.NET: Dekorierer

Das Dekorierer-Muster bietet eine Lösung dafür, dass die Zuständigkeiten (Funktionalitäten, Werte) eines Objekts dynamisch erweitert werden können. Für das Muster benötigt man zunächst eine „besonders“ abstrakte Klasse, in der die zu ergänzende Funktionalität als abstrakte Methode vorgegeben wird. Selbstverständlich besteht nachher noch die Möglichkeit, weitere Methoden oder auch Felder/Eigenschaften und damit weitere Zuständigkeiten zu entwickeln. Es ist allerdings wichtig, dass sowohl die einzelnen Dekorierer als auch das Dekorierte selbst von dieser abstrakten Klasse erben. Dann folgen wiederum zwei abstrakte Klassen, von denen die eine als Oberklassenkonzept für die dekorierten Objekte und die andere als Konzept für die Erweiterungen / Dekorationen fungiert. Dieser Artikel stellt das Muster in zwei Varianten für C#.NET dar.

Kontakt

Anrede* Herr Frau
Vorname*
Nachname*
Firma
E-Mail*
Tel-Nr.
Bereich*
Freitext

Design Patterns: Dekorierer / Decorator in C#.NET

Um ein Beispiel für die Verwendung von abstrakten Klassen zu geben und um gleichzeitig auch noch ein weiteres Entwurfsmuster vorzustellen, greifen wir auf das Dekorierer-Muster zurück. Nehmen wir sofort ganz konkret an, dass die modellierten Produkte einer Shop-Anwendung in verschiedenen Arten wie HTML, XML, Text ausgegeben werden sollen.

Dazu lassen sich zwei grundlegende Vorgehensweise denken:

  1. Die eine Möglichkeit basiert darauf, jedem Produkt seine individuelle Ausgabeart mit Hilfe einer passend benannten Methode wie liefereHTML oder liefereXML mit auf seinen Weg zu geben. Dies verhindert allerdings, dass man die Ausgabevarianten wirklich dynamisch ausführen kann, weil dann bspw. im Warenkorb unterschiedliche Methoden aufgerufen und bekannt sein müssen, wenn mal die eine oder mal die andere Ausgabeart erfolgen soll. Eine Fallunterscheidung ist hier in jedem Fall nötig. Bei wachsenden Ausgabearten müssen zudem die Klassen angepasst, weil die neue Methoden integriert werden müssen. Sind diese zusätzlich in einer abstrakten Klasse oder einer Schnittstelle bekannt, kann es sogar sein, dass Methoden implementiert werden müssen, die gar nicht weiter nützlich für die Arbeit mit dem konkreten Objekt sind, aber von der Schnittstelle her gefordert werden. Zusätzlich sind durch Änderungen, die direkt im Klassenquelltext ausgeführt werden müssen, auch immer Risiken mit neuen Fehlern verbunden.
  2. Eine andere Möglichkeit besteht darin, in einer einzigen Methode mit einem typ-Parameter zu arbeiten und diesen in einer Fallunterscheidung auszuwerten, um die passende Ausgabeart zu erzeugen. Dies erleichtert die Arbeit mit der Klasse, da jetzt keine neuen Methoden bei neuen Ausgabearten erstellt werden müssen. Allerdings ist dies auch keine gelungene Lösung, da der Parameter von seinen Werten her kommuniziert werden muss und nicht bereits am Klassenaufbau zu erkennen ist. Dann müssen Änderungen zusätzlich immer im Quelltext direkt innerhalb der Methode in der Fallunterscheidung ausgeführt werden, wenn neue Ausgabearbeiten hinzukommen. Dies fördert wiederum die Fehleranfälligkeit von Änderungen und kann sich – nicht unbedingt in diesem, aber in anderen Fällen – so ausweiten, dass die gleiche Fallunterscheidung an mehreren Stellen bei der Arbeit mit dem Objekt eingerichtet werden muss.

Diese beiden Möglichkeiten berücksichtigen allerdings nur eine komplette Ausgaben, und nicht etwa die Zuweisung von zusätzlichen Zuständigkeiten. Für einer HTML- und einer XML-Ausgabe benötigt man noch kein Dekorierer-Muster sondern gilt zunächst nur, dass die beste Lösung die Erstellung einer eigenen abstrakten Ausgabeklasse ist, von der die individuellen Ausgaben abgeleitet und an die die einzelnen Objekte, welche ausgegeben werden sollen, übergeben werden. Was in der Situationsbeschreibung noch dazu kommt, ist nun die Forderung, dass die eigentlich gleiche Ausgabe „mal so und mal so“ herauskommen soll. Dies kann sich im klassischen Beispiel immer auf die Ausgabe von Kopf- und Fußzeilen beziehen. Dafür möchte man nun sicherlich keine extra Methoden einführen oder mit mehreren Schaltern arbeiten, die individuell voneinander mit Werten versehen werden sollen.

Das Dekorierer-Muster also bietet eine Lösung dafür, dass die Zuständigkeiten (Funktionalitäten, Werte) eines Objekts dynamisch erweitert werden können, ohne die beiden gerade genannten Grundtechniken zu verwenden. Die Klassen sollen also selbst nicht geändert werden, sondern sie soll auf andere Weise neue Fähigkeiten zugewiesen bekommen. Bei der Quelltextverwendung der Zuständigkeitenklassen (Dekorierer) und der bearbeiteten Objekten (dekorierte Objekte) wird man einen Zustand erreichen, in dem man die benötigten Zuständigkeiten passend ineinander verschachtelt und das Objekt selbst für die Dekoration übergibt und so auch einen besonders gut lesbaren Quelltext und vor allen Dingen eine dynamische Verknüpfung zu erreichen.

Für das Muster benötigt man zunächst eine „besonders“ abstrakte Klasse, in der die zu ergänzende Funktionalität als abstrakte Methode vorgegeben wird. Selbstverständlich besteht nachher noch die Möglichkeit, weitere Methoden oder auch Felder/Eigenschaften und damit weitere Zuständigkeiten zu entwickeln. Es ist allerdings wichtig, dass sowohl die einzelnen Dekorierer als auch das Dekorierte selbst von dieser abstrakten Klasse erben. In unserem Fall handelt es sich um die Methode ausgeben.

Dann folgen wiederum zwei abstrakte Klassen, von denen die eine als Oberklassenkonzept für die Produkte und die andere als Konzept für die Ausgaben fungiert. In verschiedenen anderen Beispielen in der Literatur zum gleichen Muster würde die abstrakte Klasse für das Dekorierte fehlen. Da wir in unserem Shopbeispiel allerdings verschiedene Produkte verwenden, muss sich dies jetzt auch niederschlagen. Wichtig ist hier zu bemerken, dass die abstrakte Klasse für die Dekoration eine Aggregationsbeziehung mit der abstrakten Klasse von den Dekorierten hat. Diese werden also einem Dekorierer übergeben, der sie dann dekorieren bzw. in anderer Form ausgeben soll.

// Absolute Oberklasse mit Methode ausgeben
abstract public class ProduktAusgabe {
    public abstract string ausgeben();
}
// Abstrakte Klasse für Produkte für Dekoration
abstract public class Produkt : ProduktAusgabe {
    public int nr, menge;
    public Produkt(int nr, int menge) {
        this.nr = nr;
        this.menge = menge;
    }
}
// Abstrakte Klasse für Dekoratoren
abstract public class Ausgabe : ProduktAusgabe {
    protected ProduktAusgabe produkt;
    public Ausgabe(ProduktAusgabe produkt) {
        this.produkt = produkt;
    }
}
Abstrakte Klassen

Es folgen danach beliebig viele – in unserem Abdruck nur eine einzige – Klassen möglicher Produkte, die dekoriert werden sollen. Die Methode ausgeben ist hier ebenfalls enthalten, wird allerdings nur für einfache Textausgabe verwendet.

public class CD : Produkt {
    public double minuten;
    public CD(int nr, int menge, double minuten)
        : base(nr, menge) {
        this.minuten = minuten;
    }
    public override string ausgeben() {
        return "CD: " + nr + " | " + minuten;
    }
}
Konkrete Klasse für ein CD-Produkt

Danach wiederum folgen die verschiedenen Dekorierer, von denen wir hier als Beispiel zwei erstellt haben und auch zeigen. Der eine erstellt die bereits oben erwähnte Kopf- und der andere die Fußzeile. Das heißt, die in der jeweiligen Produktklasse enthaltene ausgeben-Methode wird hier tatsächlich erweitert, indem vorher und nachher zusätzliche Ausgaben gemacht werden. Diese zusätzlichen Angaben sind genau die Zuständigkeiten, von denen in der obigen Beschreibung die Rede war, weil hier eine bereits fertige (Basis-)Implementierung mit weiteren Funktionalitäten versehen wird. Das Muster eignet sich von seinem Aufbau hier besonders gut, weil tatsächlich auch physisch um die eigentliche Ausgabezeile weitere Ausgaben gemacht werden.

public class KopfZeile : Ausgabe {
    public KopfZeile(ProduktAusgabe produkt) : base(produkt) { }
    public override string ausgeben() {
        return "<h1>Produkt</h1>\n"
               + produkt.ausgeben();
    }
}
public class FussZeile : Ausgabe {
    public FussZeile(ProduktAusgabe produkt) : base(produkt) { }
    public override string ausgeben() {
        return produkt.ausgeben() + "<hr/>\n";
    }
}
Konkrete Dekorationsklassen

Unabhängig vom Aufbau der verschiedenen Klassen ist nun besonders interessant, wie denn die Forderung, dass die Zuständigkeiten dynamisch übergeben werden, sich im Klienten-Quelltext niederschlägt. Dies erkennt man besonders einfach am konkreten Beispiel. Zwei verschiedene Objekte vom Typ Buch und CD, die jeweils von Produkt und dieses wiederum von ProduktAusgabe erben, werden erzeugt und sollen jeweils ausgegeben werden. Dabei erzeugt man ein Objekt vom Typ Ausgabe, in dem man in der richtigen Reihenfolge die Zuständigkeiten und schließlich das auszugebende Objekt selbst dem Konstruktor übergibt. Dies führt dazu, dass man besonders einfach die verschiedenen Dekorierungen erkennt und auch sehr kompakt die konkrete Ausgabe einrichten kann. Im ersten Fall erzeugt man eine mit Kopf- und Fußzeile, im zweiten genügt eine einfache Kopfzeile.

Buch roman = new Buch(1254, 1, 244);
CD album = new CD(354, 1, 45.23);
Ausgabe html1 = new KopfZeile(new FussZeile(roman));
ausgabe.Text = html1.ausgeben();
Ausgabe html2 = new KopfZeile(album);
ausgabe.Text += html2.ausgeben();
Verwendung einer Dekoration

In diesem Fall lohnt sich auch einmal ein Beweisfoto vom Browser, in dem deutlich die Überschrift in h1 und die Trennungslinie des ersten Produktes zu sehen ist.

Ausgabe im Browser

Ausgabe im Browser

Das Klassendiagramm hat einen typischen Aufbau, der in unserem Beispiel allerdings auf der linken Seite um eine weitere abstrakte Klasse ergänzt wird, da es verschiedene Arten von Produkten gibt. Beide Klassen, sowohl die Dekoriererklasse als auch die der zu dekorierenden Objekte erben von der gleichen abstrakten Klasse. Dadurch ist es möglich, einem Dekorierer sowohl ein Objekt vom Typ eines Dekorierers als auch vom Typ eines Objekts zu übergeben, weil der Konstruktor in Wirklichkeit auf den gemeinsamen Obertyp wartet.

Dekorierer

Dekorierer

    Comelio GmbH C#.NET - Dekorierer-Muster - Design Patterns / Entwurfsmuster Syntax Microsoft Tutorial Programmierung Entwurfsmuster Code .NET Anleitung Beispiel C#.NET Frankfurt Duisburg Dortmund Hamburg Essen Wuppertal Berlin Münster Köln Düsseldorf Bonn BochumComelio GmbH C#.NET - Dekorierer-Muster - Design Patterns / Entwurfsmuster Syntax Microsoft Tutorial Programmierung Entwurfsmuster Code .NET Anleitung Beispiel C#.NET Frankfurt Duisburg Dortmund Hamburg Essen Wuppertal Berlin Münster Köln Düsseldorf Bonn BochumComelio GmbH C#.NET - Dekorierer-Muster - Design Patterns / Entwurfsmuster Syntax Microsoft Tutorial Programmierung Entwurfsmuster Code .NET Anleitung Beispiel C#.NET Frankfurt Duisburg Dortmund Hamburg Essen Wuppertal Berlin Münster Köln Düsseldorf Bonn BochumComelio GmbH C#.NET - Dekorierer-Muster - Design Patterns / Entwurfsmuster Syntax Microsoft Tutorial Programmierung Entwurfsmuster Code .NET Anleitung Beispiel C#.NET Frankfurt Duisburg Dortmund Hamburg Essen Wuppertal Berlin Münster Köln Düsseldorf Bonn BochumComelio GmbH C#.NET - Dekorierer-Muster - Design Patterns / Entwurfsmuster Syntax Microsoft Tutorial Programmierung Entwurfsmuster Code .NET Anleitung Beispiel C#.NET Frankfurt Duisburg Dortmund Hamburg Essen Wuppertal Berlin Münster Köln Düsseldorf Bonn BochumComelio GmbH C#.NET - Dekorierer-Muster - Design Patterns / Entwurfsmuster Syntax Microsoft Tutorial Programmierung Entwurfsmuster Code .NET Anleitung Beispiel C#.NET Frankfurt Duisburg Dortmund Hamburg Essen Wuppertal Berlin Münster Köln Düsseldorf Bonn BochumComelio GmbH C#.NET - Dekorierer-Muster - Design Patterns / Entwurfsmuster Syntax Microsoft Tutorial Programmierung Entwurfsmuster Code .NET Anleitung Beispiel C#.NET Frankfurt Duisburg Dortmund Hamburg Essen Wuppertal Berlin Münster Köln Düsseldorf Bonn BochumComelio GmbH C#.NET - Dekorierer-Muster - Design Patterns / Entwurfsmuster Syntax Microsoft Tutorial Programmierung Entwurfsmuster Code .NET Anleitung Beispiel C#.NET Frankfurt Duisburg Dortmund Hamburg Essen Wuppertal Berlin Münster Köln Düsseldorf Bonn BochumComelio GmbH C#.NET - Dekorierer-Muster - Design Patterns / Entwurfsmuster Syntax Microsoft Tutorial Programmierung Entwurfsmuster Code .NET Anleitung Beispiel C#.NET Frankfurt Duisburg Dortmund Hamburg Essen Wuppertal Berlin Münster Köln Düsseldorf Bonn Bochum
Seminare