Esta pergunta diz respeito ao uso do padrão Decorator para adicionar pouca funcionalidade a objetos de classes grandes.
Seguindo o padrão clássico do Decorator, considere a seguinte estrutura de classes:
Por exemplo, imagine que isso acontece dentro de um jogo. As instâncias de ConcreteCharacterDecorator
destinam-se a adicionar pouca funcionalidade ao ConcreteCharacter
'empacotamento'.
Por exemplo, methodA()
retorna um int
valor que representa o dano que o personagem causa aos inimigos. O ConcreteCharacterDecorator
simplesmente adiciona a esse valor. Portanto, ele só precisa adicionar código methodA()
. A funcionalidade de methodB()
permanece a mesma.
ConcreteCharacterDecorator
ficará assim:
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.
}
}
Isso não é problema com pequenas classes contendo dois métodos.
Mas e se AbstractCharacter
definidos 15 métodos? ConcreteCharacterDecorator
teria que implementar todos eles, mesmo que apenas para adicionar pouca funcionalidade.
Terminarei com uma classe que contém um método que adiciona um pouco de funcionalidade e outros 14 métodos que simplesmente delegam ao objeto interno.
Seria assim:
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.
}
}
Obviamente, muito feio.
Provavelmente não sou o primeiro a encontrar esse problema com o Decorator. Como posso evitar isso?
fonte
AbstractCharacterDelegator
'regular' (já que todos os métodos 'regulares' em Java são virtuais por padrão - o que significa que eles podem ser substituídos por subclasses herdadas)? E sua implementação simplesmente delega para o objeto interno - e se eu quiser mudar isso, simplesmente substitua-o?AbstractCharacter
em uminterface
. O restante permanece praticamente o mesmo -ConcreteCharacter
eAbstractCharacterDecorator
implementa a interface em vez de subclassificar. Tudo deve funcionar. Embora eles provavelmente não precisem estender mais nada, seus decoradores não estão presos à subclasse,AbstractCharacterDecorator
pois eles sempre podem fazer a delegação à moda antiga, se precisarem estender uma classe diferente por algum motivo.Se o AbstractCharacter tiver 15 métodos, ele provavelmente precisará ser dividido em classes menores. Além disso, não há muito mais que você possa fazer sem o idioma fornecer algum tipo de suporte especial à sintaxe para delegação. Basta deixar um comentário onde toda a delegação começa para que o leitor saiba que não precisa olhar mais para baixo. Quanto a escrever todos os métodos de encaminhamento, seu IDE pode ajudá-lo.
Além disso, é minha opinião que o Decorator funciona melhor com interfaces. Geralmente, você deseja limitar sua abstração a um número finito (possivelmente 0) de subclasses ou percorrer todo o caminho com uma interface. A herança ilimitada tem todos os inconvenientes das interfaces (você pode receber uma implementação incorreta), mas não obtém flexibilidade total (você só pode subclassificar uma coisa.) Pior ainda, isso abre o problema da classe básica frágil e é não é tão flexível quanto a composição (você cria a SubclassA e a SubclassB, mas não pode herdar de ambas para criar a SubclassAB com recursos de ambas). Por isso, acho que ser forçado a entrar em uma árvore de herança apenas para apoiar a decoração é uma farsa.
fonte