In questo articolo parleremo del pattern strategy, un altro dei pattern comportamentali del catalogo della Gang of four (GOF).
Lo strategy risulta utile e fondamentale in tutti i casi per i quali è necessario isolare un algoritmo dal resto del codice e rendere lo stesso intercambiabile a run-time con eventuali varianti differenti.
La sua implementazione risulta abbastanza semplice, abbiamo bisogno di:

  • Un’interfaccia Strategy che espone un unico metodo, executeStrategy() che si occupa di eseguire il nostro algoritmo.
  • Una o più classi ConcreteStrategy che implementano l’interfaccia e di fatto descrivono quindi l’algoritmo vero e proprio da eseguire.
  • Una classe Context principale che si occupa di eseguire il nostro algoritmo invocando il metodo “executeStrategy()” dell’apposita variabile di classe “strategy” che è un’istanza della nostra Strategy astratta.
Veniamo ai fatti in modo da comprendere al meglio di cosa stiamo parlando.
Mi sono trovato a dover gestire delle chiamate verso un server remoto di un servizio di terze parti e, a seconda delle risposte ottenute in caso di fault, avevo la necessità di gestire delle logiche di retry differenti per determinare dopo quanto tempo riprovare ad effettuare una nuova chiamata al server.
Per semplificare diciamo che avevo deciso di gestire tre casistiche principali:
  1. Riprovare a chiamare il server ogni N secondi, con N prefissato.
  2. Riprovare a chiamare il server ogni 2^N secondi andando a gestire questa serie numerica: 1, 2, 4, 8, 16, 32 e poi ripartendo da 1.
  3. Riprovare a chiamare il server solo una volta dopo N secondi.
Ciascuno di questi 3 punti rappresenta per me una Strategy da implementare, ovvero un algoritmo da realizzare per garantire il funzionamento descritto.
Non so a priori se andrò ad eseguire la Strategy uno, due o tre dato che sono i codici di risposta del server remoto che determinano la logica di retry da utilizzare.
Ovviamente dovendo rendere questi algoritmi intercambiabili a run-time, il pattern strategy diventa proprio quello di cui abbiamo bisogno.
  • Ho definito la mia interfaccia RetryStrategy esponendo il metodo doRetry();
  • Ho implementato i miei tre algoritmi implementando l’interfaccia RetryStrategy su 3 classi Strategy differenti: LinearRetry, ExponentialRetry e SimpleRetry .
A questo punto il gioco è fatto:
  • Un controller intercetta i codici di errore del server e imposta l’algoritmo da eseguire sulla mia classe ServerConnection con un apposito metodo setRetryStrategy() che valorizza la mia variabile “retryStrategy”.
  • La classe ServerConnection invoca “retryStrategy.doRetry()” e non devo far altro che affidarmi al mio algoritmo in esecuzione.
Non penso ci sia molto altro da aggiungere. Naturalmente il metodo di interfaccia della Strategy può anche essere parametrico, questo dipende soltanto dalle esigenze specifiche.