Decorator (Design Pattern): Unterschied zwischen den Versionen
K |
|||
(6 dazwischenliegende Versionen von einem anderen Benutzer werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
− | In unserer Designs ist es oft nützlich, das Verhalten eines Objektes zur Laufzeit zu erweitern, ohne vorhandenen Code ändern zu müssen. Wir können die Komposition und die Delegation benutzen, um neue Verhaltensweisen zur Laufzeit | + | In unserer Designs ist es oft nützlich, das Verhalten eines [[Objekt|Objektes]] zur [[Laufzeit]] zu erweitern, ohne vorhandenen [[Code]] ändern zu müssen. Wir können die [[Komposition]] und die [[Delegation]] benutzen, um neue Verhaltensweisen zur Laufzeit hinzuzufügen. |
− | Das Decorator Design Pattern fügt einem Objekt dynamisch Verantwortung hinzu. Zwecks Erweiterung der Funktionalität eines Objekts, bietet das Decorator Design Pattern eine flexible Alternative zur Vererbung. | + | Das Decorator Design Pattern fügt einem Objekt dynamisch Verantwortung hinzu. Zwecks Erweiterung der Funktionalität eines Objekts, bietet das Decorator Design Pattern eine flexible Alternative zur [[Vererbung]]. |
Das Decorator Design Pattern impliziert die Schaffung einer Reihe von Decorator-Klassen, welche die Komponenten umhüllen. | Das Decorator Design Pattern impliziert die Schaffung einer Reihe von Decorator-Klassen, welche die Komponenten umhüllen. | ||
Zeile 12: | Zeile 12: | ||
Nehmen wir zum Beispiel den abstrakten Obertyp "Pet" | Nehmen wir zum Beispiel den abstrakten Obertyp "Pet" | ||
− | < | + | <syntaxhighlight lang="java">package tierschau; |
public abstract class Pet | public abstract class Pet | ||
Zeile 48: | Zeile 48: | ||
} | } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
und den konkrete Untertyp "Katze", der von "Pet" abgeleitet ist: | und den konkrete Untertyp "Katze", der von "Pet" abgeleitet ist: | ||
− | < | + | <syntaxhighlight lang="java">package tierschau; |
public class Katze extends Pet | public class Katze extends Pet | ||
Zeile 69: | Zeile 69: | ||
} | } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
− | Wir haben den Namen der konkreten Klasse in der Eigenschaft "species" der abstrakten Klasse "Pet" gespeichert (um zu vermeiden, dass wir uns zu einem späteren Zeitpunkt auf die konkrete Klasse "Katze" beziehen müssen). | + | Wir haben den Namen der konkreten [[Klasse]] in der Eigenschaft "species" der abstrakten Klasse "Pet" gespeichert (um zu vermeiden, dass wir uns zu einem späteren Zeitpunkt auf die konkrete Klasse "Katze" beziehen müssen). |
Um das Verhalten eines "Pet" zu erweitern, führen wir den abstrakten Decorator "PetBehavior" hinzu. Erinnern wir uns daran, dass der Decorator den gleichen Typ hat wie die Komponente, die er umhüllt. Das ist der Grund, weshalb wir ihn von "Pet" ableiten: | Um das Verhalten eines "Pet" zu erweitern, führen wir den abstrakten Decorator "PetBehavior" hinzu. Erinnern wir uns daran, dass der Decorator den gleichen Typ hat wie die Komponente, die er umhüllt. Das ist der Grund, weshalb wir ihn von "Pet" ableiten: | ||
− | < | + | <syntaxhighlight lang="java">package tierschau; |
public abstract class PetBehavior extends Pet | public abstract class PetBehavior extends Pet | ||
Zeile 85: | Zeile 85: | ||
} | } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
Wir verwirklichen den abstrakten "Decorator" durch die zwei konkreten Implementierungen "PetSchnurrend" und "PetFauchend": | Wir verwirklichen den abstrakten "Decorator" durch die zwei konkreten Implementierungen "PetSchnurrend" und "PetFauchend": | ||
− | < | + | <syntaxhighlight lang="java">package tierschau; |
public class PetSchnurrend extends PetBehavior | public class PetSchnurrend extends PetBehavior | ||
Zeile 109: | Zeile 109: | ||
} | } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
− | < | + | <syntaxhighlight lang="java">package tierschau; |
public class PetFauchend extends PetBehavior | public class PetFauchend extends PetBehavior | ||
Zeile 132: | Zeile 132: | ||
} | } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
Beachten wir in diesen konkreten "Decorators": | Beachten wir in diesen konkreten "Decorators": | ||
* die Anwesenheit der Eigenschaft "pet", die sich auf die Komponente bezieht, die vom Decorator umhüllt wird. | * die Anwesenheit der Eigenschaft "pet", die sich auf die Komponente bezieht, die vom Decorator umhüllt wird. | ||
− | * das Hinzufügen eines neuen Verhaltens in der Methode laut() nach dem Aufruf der Methode laut() der Komponente: '''return pet.laut() + ", schnurren", ''' (in PetSchnurrend) und '''return pet.laut() + ", fauchen"; ''' (in PetFauchend) | + | * das Hinzufügen eines neuen Verhaltens in der [[Methode]] laut() nach dem Aufruf der Methode laut() der Komponente: '''return pet.laut() + ", schnurren", ''' (in PetSchnurrend) und '''return pet.laut() + ", fauchen"; ''' (in PetFauchend) |
− | Schließlich | + | Schließlich zeigen wir hier die Startklasse der Anwendung "MeineTierschau": |
− | < | + | <syntaxhighlight lang="java">package tierschau; |
public class MeineTierschau | public class MeineTierschau | ||
Zeile 156: | Zeile 156: | ||
} | } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
Das Programm gibt die folgenden Zeilen auf der Konsole aus: | Das Programm gibt die folgenden Zeilen auf der Konsole aus: | ||
Zeile 165: | Zeile 165: | ||
Am Ende sehen wir, dass die Katze sowohl von PetSchnurrend als auch von PetFauchend umhüllt wurde. Auf diese Weise könnte sie mit einer beliebige Anzahl von Decorators umhüllt werden. | Am Ende sehen wir, dass die Katze sowohl von PetSchnurrend als auch von PetFauchend umhüllt wurde. Auf diese Weise könnte sie mit einer beliebige Anzahl von Decorators umhüllt werden. | ||
+ | Damit haben wir das Verhalten der Katze zur Laufzeit erweitert, ohne vorhandenen Code ändern zu müssen. | ||
+ | |||
+ | Die '''Java I/O''' Klassen sind übrigens auch nach dem Decorator-Prinzip aufgebaut. Wir finden dort zum Beispiel den abstrakten Obertyp '''InputStream''' mit den konkreten Untertypen FileInputStream, ByteArrayInputStream, ObjectInputStream, ... Die abstrakte Decorator-Klasse heißt '''FilterInputStream''', die logischerweise auch von InputStream abgeleitet ist. Konkrete "Decorators" sind: PushbackInputStream, BufferedInputStream, DataInputStream, ... Man kann auch leicht eigene konkrete "Decorators" hinzufügen, etwa einen "LowercaseInputStream", der alle Großbuchstaben in Kleinbuchstaben umwandelt... | ||
− | |||
[[Kategorie:Design Pattern]] | [[Kategorie:Design Pattern]] | ||
+ | [[Kategorie:Design Patterns]] | ||
[[Kategorie:Gang of Four Pattern]] | [[Kategorie:Gang of Four Pattern]] |
Aktuelle Version vom 28. März 2018, 09:00 Uhr
In unserer Designs ist es oft nützlich, das Verhalten eines Objektes zur Laufzeit zu erweitern, ohne vorhandenen Code ändern zu müssen. Wir können die Komposition und die Delegation benutzen, um neue Verhaltensweisen zur Laufzeit hinzuzufügen.
Das Decorator Design Pattern fügt einem Objekt dynamisch Verantwortung hinzu. Zwecks Erweiterung der Funktionalität eines Objekts, bietet das Decorator Design Pattern eine flexible Alternative zur Vererbung.
Das Decorator Design Pattern impliziert die Schaffung einer Reihe von Decorator-Klassen, welche die Komponenten umhüllen.
- Decorator-Klassen sind vom gleichen Typ wie die Komponenten, die sie umhüllen (der Typ wird entweder durch Vererbung oder durch die Implementierung einer Schnittstelle festgelegt).
- Die "Decorators" verändern das Verhalten ihrer Komponenten durch Hinzufügen neuer Features vor, nach oder anstelle von Aufrufen von Methoden der Komponente.
- Wir können eine Komponente mit einer beliebige Anzahl von "Decorators" umhüllen.
- Die "Decorators" sind transparent für den Kunden, sofern der Kunde auf dem abstrakten Obertyp der Komponente aufbaut, und nicht auf konkreten Untertypen.
Nehmen wir zum Beispiel den abstrakten Obertyp "Pet"
package tierschau;
public abstract class Pet
{
private int age;
private String species;
public Pet(final int age)
{
this.age = age;
}
public int age()
{
return age;
}
public String species()
{
return species;
}
public void setSpecies(final String species)
{
this.species = species;
}
public abstract String laut();
@Override
public String toString()
{
return "Ich bin ein(e) " + species + "; ich bin " + age + " Jahre alt; ich kann '" + laut() + "' !";
}
}
und den konkrete Untertyp "Katze", der von "Pet" abgeleitet ist:
package tierschau;
public class Katze extends Pet
{
public Katze(final int age)
{
super(age);
super.setSpecies(getClass().getSimpleName());
}
@Override
public String laut()
{
return "miauen";
}
}
Wir haben den Namen der konkreten Klasse in der Eigenschaft "species" der abstrakten Klasse "Pet" gespeichert (um zu vermeiden, dass wir uns zu einem späteren Zeitpunkt auf die konkrete Klasse "Katze" beziehen müssen).
Um das Verhalten eines "Pet" zu erweitern, führen wir den abstrakten Decorator "PetBehavior" hinzu. Erinnern wir uns daran, dass der Decorator den gleichen Typ hat wie die Komponente, die er umhüllt. Das ist der Grund, weshalb wir ihn von "Pet" ableiten:
package tierschau;
public abstract class PetBehavior extends Pet
{
public PetBehavior(final int age)
{
super(age);
}
}
Wir verwirklichen den abstrakten "Decorator" durch die zwei konkreten Implementierungen "PetSchnurrend" und "PetFauchend":
package tierschau;
public class PetSchnurrend extends PetBehavior
{
private final Pet pet;
public PetSchnurrend(final Pet pet)
{
super(pet.age());
this.pet = pet;
setSpecies(pet.species());
}
@Override
public String laut()
{
return pet.laut() + ", schnurren";
}
}
package tierschau;
public class PetFauchend extends PetBehavior
{
private final Pet pet;
public PetFauchend(final Pet pet)
{
super(pet.age());
this.pet = pet;
setSpecies(pet.species());
}
@Override
public String laut()
{
return pet.laut() + ", fauchen";
}
}
Beachten wir in diesen konkreten "Decorators":
- die Anwesenheit der Eigenschaft "pet", die sich auf die Komponente bezieht, die vom Decorator umhüllt wird.
- das Hinzufügen eines neuen Verhaltens in der Methode laut() nach dem Aufruf der Methode laut() der Komponente: return pet.laut() + ", schnurren", (in PetSchnurrend) und return pet.laut() + ", fauchen"; (in PetFauchend)
Schließlich zeigen wir hier die Startklasse der Anwendung "MeineTierschau":
package tierschau;
public class MeineTierschau
{
public static void main(final String[] args)
{
Pet animal = new Katze(4);
System.out.println(animal);
animal = new PetSchnurrend(animal); //den Pet mit PetSchnurrend umhüllen
System.out.println(animal);
animal = new PetFauchend(animal); //den Pet mit PetFauchend umhüllen
System.out.println(animal);
}
}
Das Programm gibt die folgenden Zeilen auf der Konsole aus:
- Ich bin ein(e) Katze; ich bin 4 Jahre alt; ich kann 'miauen' !
- Ich bin ein(e) Katze; ich bin 4 Jahre alt; ich kann 'miauen, schnurren' !
- Ich bin ein(e) Katze; ich bin 4 Jahre alt; ich kann 'miauen, schnurren, fauchen' !
Am Ende sehen wir, dass die Katze sowohl von PetSchnurrend als auch von PetFauchend umhüllt wurde. Auf diese Weise könnte sie mit einer beliebige Anzahl von Decorators umhüllt werden. Damit haben wir das Verhalten der Katze zur Laufzeit erweitert, ohne vorhandenen Code ändern zu müssen.
Die Java I/O Klassen sind übrigens auch nach dem Decorator-Prinzip aufgebaut. Wir finden dort zum Beispiel den abstrakten Obertyp InputStream mit den konkreten Untertypen FileInputStream, ByteArrayInputStream, ObjectInputStream, ... Die abstrakte Decorator-Klasse heißt FilterInputStream, die logischerweise auch von InputStream abgeleitet ist. Konkrete "Decorators" sind: PushbackInputStream, BufferedInputStream, DataInputStream, ... Man kann auch leicht eigene konkrete "Decorators" hinzufügen, etwa einen "LowercaseInputStream", der alle Großbuchstaben in Kleinbuchstaben umwandelt...