giovedì 1 marzo 2012

Il pattern Decorator, un esempio concreto

Oggi parliamo del pattern decorator, un pattern strutturale del catalogo GOF.
Questo pattern risulta utile quando abbiamo la necessità di aggiungere funzionalità o modificare il comportamento di un metodo di classe e al tempo stesso non vogliamo definire una gerarchia strutturata di classi e sottoclassi che vanno in override sui vari metodi.
Il rovescio della medaglia è che andiamo a creare tante piccole classi slegate tra loro. 

Ovviamente un pattern di per se non rappresenta una buona soluzione ma lo diventa se va incontro alla nostra specifica esigenza, quindi occorre sempre valutare di volta in volta quale approccio utilizzare.

L'implementazione del pattern è organizzata in questo modo:
  • l'interfaccia Component espone i metodi operation() che siamo interessati a rielaborare.
  • le classi ConcreteComponentUno e ConcreteComponentDue implementano l’interfaccia Component e contengono il codice specifico all'interno dei metodi operation.
  • la classe AbstractDecorator implementa l'interfaccia Component e tiene un riferimento ad un oggetto Component.
  • le classi DecoratorUno e DecoratorDue estendono la classe astratta AbstractDecorator e aggiungono all'interno del metodo operation il loro contributo invocando un metodo addBehavior().

Io mi sono trovato ad utilizzarlo in questa particolare situazione:

Dovevo realizzare una semplice applicazione che, dato un file in input contenente dei numeri, andasse ad interrogare un servizio web per ciascun numero in elenco e generasse alla fine un file contenente i numeri di partenza più alcune altre informazioni fornite dal servizio web interpellato o ricavate a partire dalla specifica risposta ottenuta.

Tanto per intenderci:


Input: 12345
Output: 12345 + risposta_servizio_web + ABCD;



Nel giro di pochissimo tempo mi sono trovato a dover gestire differenti output in base al cliente finale per il quale svolgevo queste operazioni di lookup, ovvero:


Input: 12345
Output1: 12345 + risposta_servizio_web + ABCD;
Output2: ABCD + 12345 + risposta_servizio_web + 6789;



Ho pensato quindi che potesse essere utile sfruttare il pattern decorator per costruire dei wrapper in grado di aggiungere delle ulteriori informazioni a partire dalle logiche di interrogazione al servizio web.

La mia implementazione prevede i seguenti oggetti:

  • L’interfaccia LookupOperation espone il metodo doLookup()
  • La classe ServiceLookup implementa l’interfaccia LookupOperation e implementa le logiche di interrogazione al servizio web. In uscita, il metodo doLookup() genera un file csv contenente per ogni riga la coppia di valori 12345 + risposta_servizio_web.
  • La classe LookupDecorator contiene un’istanza della classe ServiceLookup, chiamiamola serviceLookup, e implementa l’interfaccia LookupOperation, in più definisce il metodo addExtraInformation() sul quale andrò in override per definire di volta in volta le logiche del Decorator concreto.
  • Le classi LookupDecoratorUno e  LookupDecoratorDue estendono LookupDecorator e all’interno del metodo doLookup, oltre ad invocare serviceLookup.doLookup(), vanno anche ad invocare il metodo addExtraInformation() per “decorare” o “wrappare” appunto l’output iniziale e generare dei nuovi file csv che vanno a sovrascrivere il file iniziale.

Quindi quello che succede a livello di codice è che vado a scrivere:


LookupDecorator dec1 = new LookupDecoratorUno(new ServiceLookup());
dec1.doLookup();



e analogamente



LookupDecorator dec2 = new LookupDecoratorDue(new ServiceLookup());
dec2.doLookup();



Come già detto, all’interno del metodo doLookup delle classi concrete che estendono LookupDecorator troviamo:


public void doLookup() {
   serviceLookup.doLookup();
   addExtraInformation();
}



Spero di aver reso l’idea di come poter usare questo pattern. 
Ripensandoci a posteriori avrei potuto implementare un template pattern o uno strategy che forse erano più calzanti per la specifica situazione ma come sappiamo il tempo non è nostro amico e certe volte occorre scegliere una strada e finalizzare il risultato.

Nessun commento:

Posta un commento