Uso del pattern Decoratore per aggiungere metodi pubblici a un oggetto

6

Il pattern Decorator viene solitamente utilizzato per estendere la funzionalità di un oggetto estendendo uno dei suoi metodi attuali.

Per illustrare, considera un oggetto object e un decoratore decorator . object ha un metodo chiamato methodA() . decorator , perché siamo nel pattern Decorator, ha anche questo metodo per ereditarietà (entrambi object e decorator alla fine ereditano dalla stessa super-classe che ha methodA() . Chiamiamola AbstractObject ).

methodA() in object assomiglia a questo:

public void methodA(){
    // do stuff
}

methodA() in decorator assomiglia a questo:

public void methodA(){
    // do some extra stuff.
    wrappedObject.methodA(); // and then delegate to the wrapped object.
}

Sarebbe usato in questo modo:

AbstractObject object = new ConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
object.methodA(); // does stuff and some more stuff. the extended methodA().

Il punto di decorator è di estendere la funzionalità di object , e lo fa estendendo uno dei metodi attuali di object . Finora, classico Decorator.

Ora, la mia domanda:

Come dicevo, Decorator di solito estende la funzionalità di un oggetto aggiungendo il contenuto a uno dei metodi correnti dell'oggetto.

Ma se volessi usare Decorator per aggiungere metodi pubblici a un oggetto ?

Considerare il seguente scenario in cui decorator ha un altro metodo methodB() :

AbstractObject object = new ConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
((ConcreteDecorator)object).methodB();
// methodB() belongs to ConcreteDecorator() but not to AbstractObject().

Come vedi, se volessi farlo, dovrei lanciare object in ConcreteDecorator .

Questo espone il tipo concreto di object e il fatto che sia decorato, e come tale sconfigge un po 'l'intento del pattern Decorator. Inoltre, il codice che richiama methodB() dovrebbe sapere che object è decorato. Di nuovo, non molto nello spirito di Decorator.

Alla luce di questo ho due domande:

  • Il motivo Decorator è mai stato utilizzato in questo modo? È mai stato usato per aggiungere metodi pubblici per un oggetto, invece di "estenderlo" solo metodi attuali?

  • Ci sono situazioni in cui questa sarebbe una buona idea o un uso saggio di Decorator?

posta Aviv Cohn 12.04.2014 - 22:54
fonte

2 risposte

7

Il pattern decoratore viene in genere utilizzato per evitare un'esplosione di sottoclassi. Un esempio comune riguarda ui windows che si può desiderare di avere una combinazione di n attributi (barra di scorrimento, barra del titolo, ridimensionabile, mobile, ecc.). Il supporto di tutte le combinazioni di questi attributi implica la creazione di sottoclassi 2 ^ n. Il modello decoratore impedisce l'esplosione avvolgendo i metodi pubblici della classe della base della finestra esistenti . In questo caso, il modello di decoratore richiederebbe solo n classi.

Per il caso che hai descritto, stai aggiungendo nuovi metodi pubblici; pertanto, lo schema del decoratore probabilmente non è l'ideale. Non riesco a pensare a nessuna situazione in cui sarebbe ideale aggiungere nuovi metodi tramite un decoratore.

Alcune alternative da considerare:

Sottotipizzazione

È valido utilizzare la sottotipizzazione per aggiungere metodi, purché si tengano presenti i principi SOLID. Pertanto, potresti prendere in considerazione l'estensione dell'oggetto concreto per supportare il metodoB e quindi progettare il decoratore attorno ad esso.

AbstractObject object = new ExtendedConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
(ConcreteDecorator)object.methodB();
// methodB() belongs to ExtendedConcreteObject() but not to AbstractObject()

Modello adattatore

Un altro modo per visualizzare il problema: aggiungendo un metodo pubblico, si modifica l'interfaccia dell'oggetto. Questo può essere fatto utilizzando il modello adattatore . Per mantenere semplice il decoratore concreto, potresti prendere in considerazione la creazione di un decoratore e quindi creare un adattatore per quel decoratore.

AbstractObject object = new ConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
object = new ConcreteAdapter(object);
(ConcreteAdapter)object.methodB();
// methodB() belongs to ConcreteAdapter() but not to AbstractObject() or ConcreteDecorator().
    
risposta data 12.04.2014 - 23:50
fonte
0

La decorazione consiste nell'aggiungere modifiche comportamentali sottili a una classe sottostante.

Penso che la tua intenzione sia di estendere effettivamente una classe. Pensa "Separazione delle preoccupazioni". Inoltre, mi viene in mente il principio "Aperto / Chiuso" (che personalmente non condivido pienamente)

Il punto di decorazione è che passi un oggetto attorno al quale supporti una determinata interfaccia. Se stai facendo cose extra con metodi extra, allora la tua applicazione dovrà "down-cast" quell'oggetto per ottenere il comportamento extra.

Il downcasting di alcuni ingegneri sembra a posto, mentre altri ingegneri ritengono che questa non sia una soluzione pulita in quanto il codice è pieno di istanze di parole chiave. Questo lega le lezioni alla tua implementazione estesa che richiede più diligenza nel refactoring.

Personalmente, il downcasting degli oggetti dovrebbe essere usato con parsimonia e dovrebbe essere nascosto il più possibile dal consumo delle classi, in quanto interrompe un'interfaccia.

Tutto quello che sto dicendo è che è davvero una domanda soggettiva. Stai attento.

    
risposta data 13.04.2014 - 00:46
fonte

Leggi altre domande sui tag