Come utilizzare il pattern Decorator per aggiungere poca funzionalità ai grandi oggetti?

8

Questa domanda riguarda l'uso del pattern Decorator per aggiungere poche funzionalità agli oggetti di classi grandi.

Seguendo il classico pattern Decorator, considera la seguente struttura di classe:

Ad esempio, immagina che questo avvenga all'interno di un gioco. Le istanze di ConcreteCharacterDecorator hanno lo scopo di aggiungere poca funzionalità al ConcreteCharacter che sono 'wrapping'.

Ad esempio, methodA() restituisce un valore int che rappresenta il danno che il personaggio infligge ai nemici. ConcreteCharacterDecorator aggiunge semplicemente a questo valore. Pertanto, è sufficiente aggiungere il codice a methodA() . La funzionalità di methodB() rimane la stessa.

ConcreteCharacterDecorator sarà simile a questo:

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.
    }

}

Questo non è un problema con le classi piccole che contengono due metodi.

Ma cosa succede se AbstractCharacter ha definito 15 metodi? ConcreteCharacterDecorator dovrebbe implementarli tutti, anche se ha solo lo scopo di aggiungere poca funzionalità.

Finirò con una classe contenente un metodo che aggiunge una piccola funzionalità e altri 14 metodi che delegano semplicemente all'oggetto interno.

Sembrerebbe di sì:

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.
    }

}

Ovviamente, molto brutto.

Probabilmente non sono il primo a incontrare questo problema con Decorator. Come posso evitare questo?

    
posta Aviv Cohn 22.03.2014 - 01:41
fonte

2 risposte

6

Semplice e veloce: AbstractCharacterDelegator non ha funzioni astratte, ma virtuali. Per impostazione predefinita, passano al carattere avvolto e possono essere sovrascritti. Quindi erediti e sostituisci solo i pochi che desideri.

Questo genere di cose non è particolarmente utile se non fai un po 'di decorazione e la tua interfaccia comune è particolarmente ampia. Questo non dovrebbe essere troppo comune. E questo approccio potrebbe non essere dalla lettera un decoratore, ma utilizza il concetto e sarebbe ben inteso come un decoratore da programmatori che conoscono il modello.

    
risposta data 22.03.2014 - 03:59
fonte
3

Se AbstractCharacter ha 15 metodi, probabilmente deve essere suddiviso in classi più piccole. A parte questo, non c'è molto altro che puoi fare senza la lingua che fornisce una sorta di supporto di sintassi speciale per la delega. Basta lasciare un commento dove inizia tutta la delega in modo che il lettore sappia che non ha bisogno di guardare oltre. Per quanto riguarda la scrittura di tutti i metodi di inoltro, il tuo IDE può aiutarti.

A parte questo, è mia opinione che Decorator funzioni al meglio con le interfacce. In generale, vuoi limitare la tua astrazione a un numero finito (possibilmente 0) di sottoclassi o andare fino in fondo con un'interfaccia. L'ereditarietà illimitata ha tutti gli svantaggi delle interfacce (potresti essere passato a un'implementazione errata) ma non ottieni la massima flessibilità (puoi solo sottoclassi una cosa). Peggio ancora, ti apre fino a Fragile Base Class Problem e non è flessibile come la composizione (si creano SubclassA e SubclassB, ma non si può ereditare da entrambi per creare SubclassAB con le funzionalità di tutti e due). Quindi trovo di essere costretto in un albero ereditario solo per supportare la decorazione per essere un kludge.

    
risposta data 22.03.2014 - 02:56
fonte

Leggi altre domande sui tag