Zum Inhalt springen

Decorator

aus Wikipedia, der freien Enzyklopädie
Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 5. März 2009 um 14:35 Uhr durch 77.234.34.135 (Diskussion) (Ein Beispiel in C#: Programm passt nun auch zur Ausgabe.). Sie kann sich erheblich von der aktuellen Version unterscheiden.

Der Decorator (auch Dekorierer) ist ein Entwurfsmuster aus dem Bereich der Softwareentwicklung und gehört zur Kategorie der Strukturmuster (Structural Patterns). Das Muster ist eine flexible Alternative zur Unterklassenbildung, um eine Klasse um zusätzliche Funktionalitäten zu erweitern. Es ist ein Entwurfsmuster der sogenannten GoF-Muster (siehe Viererbande).

Verwendung

Die Instanz eines Dekorierers wird vor die zu dekorierende Klasse geschaltet. Der Dekorierer hat die gleiche Schnittstelle wie die zu dekorierende Klasse. Aufrufe an den Dekorierer werden dann verändert oder unverändert weitergeleitet (Delegation), oder sie werden komplett in Eigenregie verarbeitet. Der Dekorierer ist dabei „unsichtbar“, da der Aufrufende gar nicht mitbekommt, dass ein Dekorierer vorgeschaltet ist.

In einer alternativen Variante kann der Dekorierer als eine Strategie eingebunden und explizit aufgerufen werden.

UML-Diagramm

Akteure

Die abstrakte Komponente definiert die öffentliche Schnittstelle für zu dekorierende Objekte. Die konkrete Komponente definiert Objekte, die dekoriert werden können.

Der abstrakte Dekorierer hält eine Referenz auf eine konkrete Komponente und bietet dieselbe Schnittstelle wie die abstrakte Komponente. Der konkrete Dekorierer definiert und implementiert eine oder mehrere spezielle Dekorationen.

Vor- und Nachteile

Die Vorteile bestehen darin, dass mehrere Dekorierer hintereinandergeschaltet werden können; die Dekorierer können zur Laufzeit und sogar nach der Instanzierung ausgetauscht werden. Die zu dekorierende Klasse ist nicht unbedingt festgelegt (wohl aber deren Schnittstelle). Zudem können lange und unübersichtliche Vererbungshierarchien vermieden werden.

Zu den Nachteilen zählt, dass eventuell viele Methoden implementiert werden müssen, die die Aufrufe lediglich weiterleiten. Weiterhin werden Erweiterungen der zu dekorierenden Klasse unabsichtlich versteckt. Manchmal behilft man sich deshalb mit einem Broadcast-Mechanismus. Schließlich wird auch der Aufbau der Dekorierer-Instanzen komplexer. Als Hilfe kann dazu das Entwurfsmuster des Erbauer dienen.

Beispiele

Verschönern von GUI-Komponenten

Ein Textfeld soll mit einer Umrahmung „dekoriert“ werden.

Zwischen dem Aufrufer und dem Textfeldobjekt wird das entsprechende Dekoriererobjekt eingefügt. Das Dekoriererobjekt erzeugt die Umrahmung und übergibt den Kontrollfluss an das Textfeld. Da der Dekorierer dieselbe Schnittstelle hat, ändert sich aus der Sicht des Aufrufers nichts.

Erweitern der Funktionalität von GUI-Komponenten

Jede Komponente stellt sich ab dem Ursprungspunkt (0, 0) dar; dies ist die eigentliche, zu dekorierende Klasse. Ein vorgeschalteter Dekorierer kann die Position verschieben, indem Darstellungs- und Mauskoordinaten-Aufrufe verändert werden.

Eine Spezialisierung davon ist ein Dekorierer, der zusätzlich eine feste Größe definiert, außerhalb derer nichts dargestellt werden kann. Eine weitere Spezialisierung erweitert den Darstellungsaufruf um einen darumliegenden Rahmen.

Ein zusätzlicher Dekorierer, der den ersten Dekorierer nochmals dekoriert, kann die Komponente unsichtbar oder abgeschaltet werden lassen, indem Darstellungs- und Mausabfrage-Methoden wahlweise abgeblockt werden.

An diesem Beispiel sieht man auch, dass sich Vererbung und Delegation nicht ausschließen.

Ein Beispiel in C#

In einem Abenteuerspiel gibt es Spielfiguren. Jede davon kann Drohungen ausstoßen. Eine konkrete Spielfigur ist beispielsweise ein Monster. Spielfiguren können sich zur Laufzeit Schnupfen und Husten einfangen – dann geht ihren Drohungen ein Schniefen bzw. Husten voraus.

Das folgende Programmbeispiel zeigt, wie das Dekorierer-Muster benutzt wird, um ein Monster zu verschnupfen und zu verhusten.

Wichtig dabei ist zu verstehen, dass nicht die Unterklassen VerschnupftesMonster, VerhustetesMonster, VerschnupftesVerhustetesMonster und VerhustetesVerschnupftesMonster gebildet werden. Dieser Ansatz würde bei Hinzunahme weiterer Attribute wie „wahnsinnig“ und „feuerspuckend“ zu einer exponentiellen Zunahme der Unterklassen führen – ganz abgesehen von unerträglichen Klassennamen wie VerhustetesVerschnupftesWahnsinnigesFeuerspuckendesMonster. Es werden nur die (Reihenfolgen von) Dekorationen erzeugt, die tatsächlich benötigt werden.

Man sieht zudem, dass der Client-Code von den Dekorierern nichts weiß, nachdem die dekorierten Objekte erzeugt wurden. Aus Sicht des Client-Codes lautet der Aufruf zum Drohen immer Spielfigur.Drohe().

using System;

namespace DekoratorMuster {

  abstract class Spielfigur {
    abstract public void Drohe();
  }

  class Monster : Spielfigur {
    override public void Drohe() {
      Console.WriteLine("Grrrrrrrrrr.");
    }
  }
		
  abstract class  Dekorierer : Spielfigur {	
    private Spielfigur meineFigur;

    public Dekorierer(Spielfigur s) { meineFigur = s; }
	
    override public void Drohe() {
      meineFigur.Drohe();
    }
  }

  class HustenDekorierer : Dekorierer {
    public HustenDekorierer(Spielfigur s) : base(s) { }
		
    override public void Drohe() {
      Console.Write("Hust, hust. ");
      base.Drohe();
    }
  }

  class SchnupfenDekorierer : Dekorierer {
    public SchnupfenDekorierer(Spielfigur s) : base(s) { }

    override public void Drohe() {
      Console.Write("Schniff. ");
      base.Drohe();
    }
  }

  class ClientCode
  {
    public static void Main()
    {
      Spielfigur meinMonster = new Monster();
      meinMonster.Drohe();

      Spielfigur meinVerhustetesMonster = new HustenDekorierer(meinMonster);
      meinVerhustetesMonster.Drohe();

      Spielfigur meinVerschnupftesMonster = new SchnupfenDekorierer(meinMonster);
      meinVerschnupftesMonster.Drohe();
			
      Spielfigur meinVerschnupftesVerhustetesMonster = new SchnupfenDekorierer(new HustenDekorierer(meinMonster));
      meinVerschnupftesVerhustetesMonster.Drohe();

      Spielfigur meinVerhustetesVerschnupftesMonster = new HustenDekorierer(new SchnupfenDekorierer(meinMonster));
      meinVerhustetesVerschnupftesMonster.Drohe();
    }
  }
}

Die Ausgabe dieses Programms ist:

Grrrrrrrrr.
Hust, hust. Grrrrrrrrrr.
Schniff. Grrrrrrrrrr.
Schniff. Hust, hust. Grrrrrrrrrr.
Hust, hust. Schniff. Grrrrrrrrrr.

Ähnlich lassen sich Beispiele in C++ und anderen objektorientierten Programmiersprachen erstellen.

Verwandte Muster

Im Vergleich zum Dekorierer, welcher die tatsächlich zu verwendende Klasse selbst wählt, muss bei dem Strategie-Muster die aufrufende Instanz explizit wissen, welche Varianten zur Verfügung stehen und welche daraus verwendet werden soll.

Ähnlich dem Dekorierer ist zudem noch das Composite-Muster.

Vorlage:Navigationsleiste Entwurfsmuster der Viererbande