Design Patterns: i patterns comportamentali secondo la Gang Of Four

Design Patterns: Elements of Reusable Object-Oriented Software

Design Patterns – Gang of Four

Design Patterns: Elements of Reusable Object-Oriented Software è stata la prima raccolta comprensiva dei Design pattern per la progettazione di software utilizzando la progettazione ad oggetti all’insegna della massima flessibilità e riusabilità del codice.

I design pattern sono infatti dei pattern, delle soluzioni generiche da applicare in determinati casi, soluzioni eleganti architetturali ottimizzate all’insegna appunto della flessibilità, della riusabilità.

Il libro pubblicato nel 1995, è oggi ancora attuale (tranne forse nel linguaggio utilizzato per gli esempi, lo Smalltalk), ed è tra i più popolari libri sui design pattern (ne esistono molti). In particolare ho deciso di scrivere questa serie di articoli dopo aver letto, su un annuncio di lavoro, che veniva espressamente richiesta la conoscenza dei design pattern definiti dai GoF, ovvero i design pattern definiti in questo libro (il libro è stato scritto da quattro differenti autori, successivamente conosciuti come Gang Of Four, abbreviato in GoF).

Nel libro, i design pattern vengono suddivisi in 3 categorie:

  • Design pattern creazionali
  • Design pattern strutturali
  • Design pattern comportamentali

In questo secondo articolo saranno affrontati i design pattern comportamentali. Per approfondire la tua conoscenza dell’argomento, puoi acquistare il libro in inglese oppure la sua Versione in italiano.

I design pattern comportamentali

I design pattern strutturali sono quei design pattern che riguardano la comunicazione tra oggetti che devono cooperare, garantendone la flessibilità. Sono particolarmente importanti nei software con architetture strutturate, laddove ridurre l’accoppiamento aiuta a ridurre l’hard coding (la scrittura all’interno dei metodi di classe) delle comunicazioni e le dipendenze tra classi.

Chain-of-responsibility

Design pattern utilizzato dove una richiesta può essere presa in carico da vari oggetti, senza che il richiedente li conosca o senza che essi si conoscano tra di loro, creando un forte disaccoppiamento tra questi.

Si implementa creando una catena (implementabile come lista circolare, ad esempio con un riferimento al prossimo elemento della lista, che nel caso dell’ultimo è il primo elemento) all’interno di un oggetto al quale il client effettua la sua richiesta, poi questo oggetto utilizzando la lista circolare inoltrerà a chi di dovere, secondo una qualche logica di scheduling, prenderà in carico la richiesta, ovvero un ConcreteHandler, che può anche essere di vario tipo.

Command

Si realizza costruendo oggetti che rappresentano azioni o eventi, incapsulando eventuali parametri o informazioni dell’azione/evento. Un possibile svantaggio di questo pattern è appunto la proliferazione di molti oggetti che rappresentano tutti i possibili eventi o le possibili azioni.

Si implementa predisponendo un Receiver per gli eventi/azioni che sono oggetti che implementano l’interfaccia Command, cioè i ConcreteCommand, attraverso i quali utilizzerò l’Invoker i cui metodi sono utilizzati dal client per eseguire le azioni incapsulate dai command.

Iterator

Design che permtte di accedere agli elementi di un oggetto aggregato (insieme di oggetti) in modo sequenziali, senza esporre all’esterno l’oggetto aggregato. Occorre fare attenzione affinchè l’iterator sia thread safe. Le operazioni comuni che vanno implementate su un iterator sono hasNext() e next(); addizionalmente potrei aggiungerne altre come remove() e add(), si parla di un design più evoluto e su cui comunque bisogna fare attenzione per via della concorrenza. Ovviamente l’iterator al suo interno deve avere un indice.

Mediator

Pattern che regola la comunicazione tra oggetti, i quali non comunicano più direttamente tra di loro ma appunto utilizzando il Mediator, un oggetto specifico.

E’ un pattern usato in sistemi che utilizzando un protocollo di invio-ricezione di messaggi, come chat room.

Il vantaggio principale è dato dal disaccoppiamento tra gli oggetti coinvolti nel processo di comunicazione mediato, che si collegano solo al mediator.

Il mediator permette di concentrarsi sull’interazione del singolo oggetto all’interno del sistema, e permette inoltre di inserire un controllo centralizzato, o ancor meglio una definizione centralizzata del protocollo di comunicazione. Questo è allo stesso tempo un punto debole del pattern in quanto l’architettura del Mediator è complessa e si tratta di un oggetto complesso da scrivere, mantenere, e che può costituire un collo di bottiglia.

Memento

Design pattern che “viola” l’incapsulamento permettendo di salvare lo stato interno di un oggetto in previsione di un’operazione di restore() successiva. Vengono creati degli snapshot, ad esempio può essere utile per realizzare transazioni o operazioni undo/redo.

Si implementa con un oggetto Originator responsabile della creazione/snapshot di oggetti che implementano l’interfaccia Memento.

Observer

Pattern che permette ad alcuni oggetti di pubblicare infomazioni e ad altri di iscriversi ad un contenuto che potrebbe essere pubblicato. Esiste quindi un’interfaccia Observer implementata dagli oggetti che desiderano ricevere informazioni sul cambiamento di stato (operazione publish).

Tutti gli oggetti che implementano tale interfaccia devono quindi iscriversi presso un altro oggetto, che terrà in una struttura tutti gli Observer, e che provvederà ad informarli utilizzando un metodo (es. update() ) ogni qualvolta avverà un cambio di stato o comunque una qualche operazione che andrà a loro notificata. Il cambio di stato potrebbe avvenire all’interno di questo oggetto o anche all’esterno.

State

Pattern che permette ad un oggetto di modificare il suo comportamento a seconda di quello che è il suo stato interno: utilizzando l’ereditarietà avrò differenti classi che rappresentano differenti stati interni, e posso modificarle a runtime.

I benefici di questo pattern è un codice più pulito, avendo una classe per ogni stato, ma al tempo stesso ho più classi e questo potrebbe essere un problema se le classi diventassero molto numerose.

Avrò una certa interfaccia implementata da tutti gli stati dell’oggetto, e a seconda di una determinata logica imposterò quale degli oggetti che implementano State utilizzare per eseguire un certo metodo dell’oggetto stesso.

Strategy

Pattern utilizzato per definire una famiglia di algoritmi, utilizzabili in modo interscambiabile, a seconda di quale client ne fa richiesta o dei parametri della richiesta. In questo modo il client può anche non sapere quale algoritmo sarà utilizzato, la selezione dell’algoritmo è predisposta tutta all’interno dell’oggetto che riceve la richiesta. In alternativa il client può conoscere quali sono le strategie possibili, ed effettuare una richiesta appropriata a seconda delle circostanze.

La strategia da applicare può quindi essere impostata dall’esterno, dal client stesso o con un metodo col quale imposta la strategy o con una chiamata specifica, oppure può essere automaticamente assegnata a seconda dei parametri della chiamata


Se desideri maggiori informazioni sui design pattern creazionali, ti suggerisco di acquistare il libro della GoF su Amazon (al miglior prezzo).

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *