Diese Frage betrifft die Verwendung des Decorator-Musters, um Objekten großer Klassen wenig Funktionalität hinzuzufügen.
Beachten Sie nach dem klassischen Decorator-Muster die folgende Klassenstruktur:
Stellen Sie sich zum Beispiel vor, dies passiert innerhalb eines Spiels. Instanzen von ConcreteCharacterDecorator
sollen dem ConcreteCharacter
"Wrapping" wenig Funktionalität hinzufügen .
Gibt beispielsweise methodA()
einen int
Wert zurück, der den Schaden darstellt, den der Charakter Feinden zufügt. Das ConcreteCharacterDecorator
erhöht einfach diesen Wert. Daher muss nur Code hinzugefügt werden methodA()
. Die Funktionalität von methodB()
bleibt gleich.
ConcreteCharacterDecorator
wird so aussehen:
class ConcreteCharacterDecorator extends AbstractCharacterDecorator{
ConcreteCharacter character;
public ConcreteCharacterDecorator(ConcreteCharacter character){
this.character = character;
}
public int methodA(){
return 10 + character.methodA();
}
public int methodB(){
character.methodB(); // simply delegate to the wrapped object.
}
}
Dies ist kein Problem bei kleinen Klassen mit zwei Methoden.
Aber was ist, wenn AbstractCharacter
15 Methoden definiert sind? ConcreteCharacterDecorator
müsste sie alle implementieren, obwohl es nur dazu gedacht ist, wenig Funktionalität hinzuzufügen.
Am Ende werde ich eine Klasse haben, die eine Methode enthält, die ein wenig Funktionalität hinzufügt, und weitere 14 Methoden, die einfach an das innere Objekt delegieren.
Es würde so aussehen:
class ConcreteCharacterDecorator extends AbstractCharacterDecorator{
ConcreteCharacter character;
public ConcreteCharacterDecorator(ConcreteCharacter character){
this.character = character;
}
public int methodA(){
return 10 + character.methodA();
}
public int methodB(){
character.methodB(); // simply delegate to the wrapped object.
}
public int methodC(){
character.methodC(); // simply delegate to the wrapped object.
}
public int methodD(){
character.methodD(); // simply delegate to the wrapped object.
}
public int methodE(){
character.methodE(); // simply delegate to the wrapped object.
}
public int methodF(){
character.methodF(); // simply delegate to the wrapped object.
}
public int methodG(){
character.methodG(); // simply delegate to the wrapped object.
}
public int methodH(){
character.methodH(); // simply delegate to the wrapped object.
}
public int methodI(){
character.methodI(); // simply delegate to the wrapped object.
}
public int methodJ(){
character.methodJ(); // simply delegate to the wrapped object.
}
public int methodK(){
character.methodK(); // simply delegate to the wrapped object.
}
public int methodL(){
character.methodL(); // simply delegate to the wrapped object.
}
public int methodM(){
character.methodM(); // simply delegate to the wrapped object.
}
public int methodN(){
character.methodN(); // simply delegate to the wrapped object.
}
public int methodO(){
character.methodO(); // simply delegate to the wrapped object.
}
}
Offensichtlich sehr hässlich.
Ich bin wahrscheinlich nicht der erste, der dieses Problem mit dem Dekorateur hat. Wie kann ich das vermeiden?
quelle
AbstractCharacterDelegator
"regulär" zu setzen (da alle "regulären" Methoden in Java standardmäßig virtuell sind - was bedeutet, dass sie durch das Erben von Unterklassen überschrieben werden können)? Und lassen Sie ihre Implementierung einfach an das innere Objekt delegieren - und wenn ich das ändern möchte, überschreiben Sie sie einfach?AbstractCharacter
in ein verwandelninterface
. Der Rest bleibt weitgehend gleich -ConcreteCharacter
undAbstractCharacterDecorator
implementieren Sie die Schnittstelle anstelle von Unterklassen. Alles sollte einfach funktionieren. Obwohl sie wahrscheinlich nichts anderes erweitern müssen, sind Ihre Dekorateure nicht an Unterklassen gebunden,AbstractCharacterDecorator
da sie die Delegierung immer auf die altmodische Weise durchführen können, wenn sie aus irgendeinem Grund jemals eine andere Klasse erweitern müssen.Wenn AbstractCharacter über 15 Methoden verfügt, muss es wahrscheinlich in kleinere Klassen unterteilt werden. Abgesehen davon gibt es nicht viel anderes, als dass die Sprache eine spezielle Syntaxunterstützung für die Delegierung bietet. Hinterlassen Sie einfach einen Kommentar, in dem die gesamte Delegation beginnt, damit der Leser weiß, dass er nicht weiter nach unten schauen muss. Was das Schreiben aller Weiterleitungsmethoden betrifft, kann Ihre IDE dort helfen.
Abgesehen davon bin ich der Meinung, dass Decorator am besten mit Schnittstellen funktioniert. Im Allgemeinen möchten Sie Ihre Abstraktion entweder auf eine endliche (möglicherweise 0) Anzahl von Unterklassen beschränken oder eine Schnittstelle verwenden. Unbegrenzte Vererbung hat alle Nachteile von Schnittstellen (Ihnen könnte eine falsche Implementierung übergeben werden), aber Sie erhalten nicht die volle Flexibilität (Sie können nur eine Unterklasse unterteilen). Schlimmer noch, es öffnet Sie für das Problem der fragilen Basisklasse und es ist nicht so flexibel wie Komposition (Sie erstellen SubclassA und SubclassB, können dann aber nicht von beiden erben, um SubclassAB mit Funktionen von beiden zu erstellen). Ich finde es also ein Kludge, in einen Erbbaum gezwungen zu werden, nur um die Dekoration zu unterstützen.
quelle