[Design Pattern] Strukturelle Muster: Decorator


Thomas Darimont

Premium-User
Hallo,

dieser Beitrag erklärt das Strukturelle Muster: Decorator
Java:
package de.tutorials.design.patterns.structural;

import org.jpatterns.gof.DecoratorPattern.ConcreteDecorator;
import org.jpatterns.gof.DecoratorPattern.Decorator;

public class DecoratorExample {
  
  public static void main(String[] args) {
    WrappableWithClothes human = new Jacket(new Pullover(new TShirt(new Undershirt(new Human(36.0)))));
    
    System.out.println("Human Body Temperature: " + human.getTemperature());
  }
  
  static class Human implements WrappableWithClothes{
    private double temperature;
    
    public Human(double temperature) {
      this.temperature = temperature;
    }

    public double getTemperature() {
      return temperature;
    }    
  }
  
  static interface WrappableWithClothes{
    double getTemperature();
  }

  
  @Decorator
  static abstract class Clothing implements WrappableWithClothes{
    protected WrappableWithClothes wrappable;
    
    public Clothing(WrappableWithClothes wrappable) {
      this.wrappable = wrappable;
    }

    public double getTemperature(){
      return wrappable.getTemperature() + getTemperatureContribution();
    }
    
    public double getTemperatureContribution(){
      return 0.0;
    }
  }
  
  @ConcreteDecorator
  static class Undershirt extends Clothing {
    public Undershirt(Human human) {
      super(human); //naked
    }
    @Override
    public double getTemperatureContribution() {
      return super.getTemperatureContribution() + 0.05;
    }
  }
  
  @ConcreteDecorator
  static class TShirt extends Clothing {
    public TShirt(Clothing beneath) {
      super(beneath);
    }
    
    @Override
    public double getTemperatureContribution() {
      return super.getTemperatureContribution() + 0.1;
    }
  }
  
  @ConcreteDecorator
  static class Pullover extends Clothing {
    public Pullover(Clothing beneath) {
      super(beneath);
    }
    
    @Override
    public double getTemperatureContribution() {
      return super.getTemperatureContribution() + 0.2;
    }
  }
  
  @ConcreteDecorator
  static class Jacket extends Clothing {

    public Jacket(Clothing beneath) {
      super(beneath);
    }
    
    @Override
    public double getTemperatureContribution() {
      return super.getTemperatureContribution() + 0.5;
    }
  }
}
Ausgabe:
Code:
Human Body Temperature: 36.85
Gruß Tom
 

BE23532

Grünschnabel
Hallo,

ich bin zufällig auf dieses Forum gestoßen und hoffe, dass man mir helfen kann.

Ich habe die Beschreibungen der Design Patterns und die Code Beispiele mehrmals durchgelesen,
allerdings schaffe ich es nicht bei einem vorgegeben Code rauszufinden um welchen Design Pattern
es sich handelt. Ich habe die Vermutung das es der Decorator ist aber kann wie gesagt nie sicher sein.
Gibt es im Forum eine spezielle "Frage Ecke" oder kann ich meinen Code hier posten?

Vielen Dank schonmal.
 

Thomas Darimont

Premium-User
Hallo,

poste das Beispiel doch einfach hier - manchmal ist es schwer die Design Patterns auseinander zu halten z.Bsp. Proxy und Decorator.

Gruß Tom
 

BE23532

Grünschnabel
Hier ist auf den Seiten 1-3 der Code.
Wie man auf Seite 3 sieht geht es darum rauszufinden welches Muster dieser Code verwendet.
Ich schwanke zwischen Strategie und Dekorierer. Beobachter kann ich sicher ausschließen.
 

Anhänge

Akeshihiro

Erfahrenes Mitglied
Also ich kann da keines der drei Pattern wiederfinden ...

Beobachter ist es auf keinen Fall, da hast du recht.

Ein Decorator ist es aber ebenfalls nicht, da das komplette Gerüst für ein Decorator-Pattern fehlt. Sonnenblume implementiert lediglich das Interface Bild, mehr aber auch nicht. Das wäre also eine konkrete Implementierung. Daneben bräuchtest du eigentlich noch eine abstrakte Implementierung einer Decorator-Klasse, die ebenfalls das Inteface implementiert (damit der Decorator als Bild auftreten kann) und davon abgeleitet kämen dann die konkreten Decorator. Davon fehlt hier jedoch jegliche Spur.

Bei Strategy wird es schon etwas schwerer, denn eine gewisse Gemeinsamkeit ist gegeben. Aber beim Strategy-Pattern ist der Knackpunkt der, dass man von Außerhalb des Kontextes (Sonnenblume) die Strategie für den Kontext ändern kann. Das ist der Sinn von Strategy, man kann zur Laufzeit die Strategie und somit das Verhalten ändern. Das ist hier ebenfalls nicht der Fall. Die Methoden delegieren zwar die Aufrufe an ein Objekt vom Typ PImage, das ist dem Strategy-Pattern also sehr ähnlich, aber der Grundgedanke fehlt. Das ist in meinen Augen bestenfalls eine gekapselte Delegation.
 
Zuletzt bearbeitet:

Thomas Darimont

Premium-User
Hallo,

ich sehe im Programm unter 1. auch nur die Aspekte:
- Kapselung
- Schnittstellen
- Über Komposition (PImage in Sonnenblume) ließe sich Diskutieren:

Laut wikipedia: http://de.wikipedia.org/wiki/Komposition_(UML)#Komposition
"Die Komposition (composite aggregation oder composition) als Sonderfall der Aggregation beschreibt ebenfalls die Beziehung zwischen einem Ganzen und seinen Teilen. Der Unterschied zur Aggregation ist, dass die Existenz eines Objekts, das Teil eines Ganzen ist, von der Existenz des Ganzen abhängig ist."

Wenn also die Sonnenblume von der existenz eines PImage abhängig ist - ohne PImage gibt es keine Bild-Daten - dann liegt eine Komposition vor.

Die genannten Patterns sind IMHO im Beispiel nicht enthalten.



Gruß Tom
 

takidoso

Erfahrenes Mitglied
Hi Tom
Im Ganzen stimme ich zu.
jedoch ...
...
Wenn also die Sonnenblume von der existenz eines PImage abhängig ist - ohne PImage gibt es keine Bild-Daten - dann liegt eine Komposition vor.
...
sehe ich irgendwie genau anders herum.
Wenn Sonnenblume nicht existiert, existiert auch PImage nicht.
Würde also PImage noch existieren, wenn das PImage aufnehmende Objekt Sonnenblume nicht mehr existiert, weil irgendwo die Bilddaten noch woanders, .B. in einem Cache, Verwendung finden, dann wäre es keine Komposition.
Also würde ich PImage als Teil und die Sonenblume als das Ganze ansehen und würde bei einer Komposition mit Löschen der Sonnenblume auch der Teil der Bildaten (PImage) gelöscht werden.
 

slowfly

Erfahrenes Mitglied
Das Design einer Applikation gibt vor, wie die Applikation implementiert werden soll. Wenn man eine Komposition designed und beim Löschen des Ganzen seine Teile nicht löscht, dann wurde es eben falsch implementiert ;)

Aber ich glaube, es kommt nicht so oft vor, dass man eine Komposition designed und peinlichst darauf achtet, dass es auch so implementiert wird,...

Gruss
slowy
 

takidoso

Erfahrenes Mitglied
also ich glaube wir reden da nun etwas auseinander.
Eine typische Situation einre Komposition könnte zum beispiel ein Stuhl sein in Bezug seiner Bestandteile (Lehne, Beine etc.) Kompositionen kommen aus meiner Sicht häfig vor one dass man sich nähee Gedanken macht.
Geht der Stuhl in die Schrottpresse oder ins Feuer sind auch seine Beinde und seine Lehne dran :)
Was zum Beispiel keine Komposition ist, wäre eine Auto und eine Garage. Es ist eine lockere Assoziation, denn man muss ja nicht immer dasselbe Auto in die Garage stellen, und kommt dass Auto in die Schrottpresse steht die Garage imme noch da :)
 
Zuletzt bearbeitet:

slowfly

Erfahrenes Mitglied
Ein Stuhlbein kann man austauschen ;-) -> unabhängig davon, dass es schwierig ist ein Beispiel hinzubekommen, das ohne irgend eine Ausnahme auskommt:

Mir geht es mit meinen Aussagen darum, dass eine Komposition in Java "schwierig" (oder eher mühsam) zu implementieren ist. Nehmen wir mal das Beispiel Haus -> Raum.
Code:
class Haus{
  private List<Raum> räume;

  get/setRäume
}
Das ist schon mal falsch, das würde bedeutet, dass ich irgendwo "new Raum" machen muss -> schon existiert ein Raum ohne Haus, was dann keine Komposition ist.

Dann könnte man sowas machen:
Code:
class Haus{
  private List<Raum> räume;

  void addRaum(String raumname, Stockwerk stockwerk, whatever...){
  }

  private class Raum{
    public Raum(Haus parent, ...){
    }
  }
}
Nur hat man dann eine private class, mit welchem nur das Haus arbeiten kann. Raum.neuStreichen() geht jetzt nicht, ich müsste über Haus.streichen(Raum, Farbe) gehen, was auch nicht geht, da es ja eine private class ist.

Und wenn man Raum public macht und über eine getter-Methode an die Räume kommt, passiert sowas:
Code:
  Haus haus = new Haus();
  haus.addRaum(bla);
  haus.addRaum(noch einer);
  List<Raum> räume = haus.getRäume();
  haus = null;
Was passiert? Haus erstellen, Räume hinzufügen, Räume holen, Referenz von haus auf null setzen. Und nun kommt der GarbageCollector und räumt das Haus auf -> es existiert nicht mehr physisch. Jedoch die Räume existieren -> also keine Komposition.

Nunja, vielleicht sehe ich das etwas zu streng,... aber egal, my two pence ;)

Gruss,
slowy
 

takidoso

Erfahrenes Mitglied
Hi slowy,
also interessantes Beispiel mit dem Haus und den Räumen. Es verdeutlicht gut was Du meinst.
Also beim lesen was eine Komposition sei, stoße ich auf die generelle Beschreibung, dass ein Teil eines gesammten vergeht so das Gesammte nicht mehr existiere.
Es wird nicht darauf Bezug genommen in wie fern die Klasse selbst, die als Teil verwendet wird, nur in der oder einer Klasse vorkommen darf, die als Gesamtes fungiert.
Es wird auch nichts darüber erzählt, ob das Teilstück nicht auch außerhalb des Gesamtobjekts angesprochen werde darf (pivate/public etc).
In Deinem letzten Beispiel Haus und dem Vermerk, dass die Gabagecollection zuschlägt und in Deine Räume dann natürlich noch stehen bleibt ist tatsächlich erstmal ein Beleg einer implementatorischen Lücke. Sie gilt dann aber nur solange bis die Routine, die sich mal die Liste der Räume gekrallt und aufgehoben hat aufhört, vorausgesetzt, sie egibt die Refferenz auf die Raumliste nicht irgendwie weiter. Ich finde man sollte hier über die Eigenschaft von Java, selbst mit Garbagecolletion aufzuräumen etc. hinwegsehen, oder?
Aus meiner Sicht ist die Intenstion der Modelierung entscheident, nicht unbedingt die Lücke, die innerhalb eines Angebots der spezifischen Sprache übrig bleiben mag.
Wenn man also in dem Haus/Raum Beispiel sicher gehen möchte, dass ja niemals nicht ein Raum außerhalb eines Objektes Haus behalten werden kann, müssen halt alle Manipulationen, die bei einem Raum möglich sind, über (z.B.) Haus delegiert werden, und die Räume selbst dürften dann halt weder einzeln noch als Liste geholt werden können: (also eitwas wie getRaum() oder getRäume() gibt es dann halt nicht).
Ich wage sogar zu meinen, dass Raum nicht unbedingt eine private Klasse sein muss. Kann ja sein dass man Räume auch gerne für sich selbst oder für andere Situatioen verwenden können möchte, was an der "Komposition" Haus dann auch nichts änden würde.

Mit philosophischen Grüßen
Takidoso
 
Zuletzt bearbeitet:

slowfly

Erfahrenes Mitglied
Hallo

Ich finde man sollte hier über die Eigenschaft von Java, selbst mit Garbagecolletion aufzuräumen etc. hinwegsehen, oder?
Hmm,... nein, das denk ich nicht. Ich kann dir auch ein Design machen, in welcher Klasse A von den abstrakten Klasse B und C erbt. In Java geht das nicht, in anderen Sprachen schon.

Es gibt natürlich vieles, was eine Sprache anbietet, was man mit UML Klassendiagrammen auch designen kann: Klassen, Interfaces, Abstraktionen, Beziehungen etc. Aber wenn ich jetzt solche Dinge wie einen XOR-Constraint designe (Klasse A hat entweder Klasse B oder Klasse C, aber weder beide zusammen noch keinen), bietet Java keine Notierung an, die das macht. Dann muss ich halt im Konstruktor schauen, dass mindestens und maximal eines der Übergabeparameter nicht null ist. Genau so wenn ich die Werte über setter neue setze. Setze ich den Wert B, der null ist, auf einen Wert, der nicht null ist, muss ich den Wert C auf null setzen, da ich sonst das Design verletze.

Oder ein WSDL kann auch Multiplizitäten definieren - wie in UML -, welche ich im Code mit Annotations des entsprechenden Frameworks setzen kann. Dann wird so eine "Implementierung eines Designs" (z.B. mindestens ein Artikel muss in einer Bestellung drin sein) vom Framework übernommen. Wäre dem nicht so, müsste den Request vorher manuell validieren und bei Falscheingaben entsprechend reagieren.

Gruss
slowy
 

takidoso

Erfahrenes Mitglied
Ich glaube wir sind nun irgendwie beim abdrifften vom ursprünglicen Thema....
In meinem ersten Post ging es mir doch nur darum zu sagen, das in dem obigem Beispiel einer Komposition nicht die Sonenblume von den Bilddaten, sonder die Bildaten von der Sonnenblume abhängt. Wird also ein objekt Sonnenblume gekillt (Sonenblume als ganzes betrachtet, gehen auch ihre Bilddaten PIage übern Jordan.
Die Aussage von Tom
"Wenn also die Sonnenblume von der existenz eines PImage abhängig ist - ohne PImage gibt es keine Bild-Daten - dann liegt eine Komposition vor." würd aus meinr Sicht die Verhältnisse um drehen. Demnach wären die Bilddaten das Ganze und Sonnenblume ein Teil davon.
Der Code sagt aber irgendwie was anderes, oder?
Wie siehst Du das, slowy?