Che cosa conta davvero nei design pattern

Un post di Pietro Martinelli, con le illustrazioni di Andrea Gadaldi

Una delle attività di cui mi occupo in CodicePlastico consiste nell’organizzare e tenere sessioni di consulenza e formazione su varie tematiche, alcune più tecnologiche (Docker, MongoDb, Git, Java, …) ed altre più teoriche (Design Pattern, Principi di Design, Testing). Questo mi dà l’occasione di riflettere da punti di vista sempre diversi su questioni che sono il pane quotidiano di un ingegnere del software; il tema dei design pattern è uno di questi – e vorrei cogliere l’occasione di questo post per condividere una consapevolezza che ho maturato di recente in proposito.

Come da definizione di Wikipedia, un design pattern è una soluzione generica, riutilizzabile ed accettata ad un problema di design comune e ricorrentesi tratta cioè di una sorta di template che può essere d’aiuto nel risolvere un problema di una classe nota.

Cominciamo dal “Principio”

CATALOGO

Quando ne parlo, cerco di insistere sul fatto che, al di là del nome che diamo ai pattern presi da questo o da quel catalogo famoso, al di là del ricordare o meno i dettagli di design che ciascun pattern suggerisce, è interessante notare come, dallo studio di pattern comunemente accettati, emerga una serie di principi astratti la cui applicazione sistematica fa emergere le scelte di design descritte nei pattern: l’idea è che chi cercasse di risolvere lo stesso problema che rappresenta il contesto di un pattern senza sapere nulla di pattern ma applicando quei principi giungerebbe con ogni probabilità ad una soluzione identica – o molto simile – a quella che il pattern in questione standardizza.

principi cui faccio riferimento sono, tra gli altri, le linee guida GRASP per l’assegnazione di responsabilità ed i principi SOLID.

Se dunque la conoscenza e l’applicazione di principi astratti porta all’adozione di soluzioni di design analoghe a quelle suggerite da design pattern più o meno noti, qual è il motivo per cui è importante studiare, analizzare ed interiorizzare i cataloghi di pattern più noti?

Costruire un vocabolario comune

Per come la vedo io, il motivo principale consiste nell’acquisizione e nella disponibilità di un framework di comunicazione: se parlo ad un collega o ad un cliente di decorator la mia comunicazione è molto più rapida, precisa ed efficace di quanto non sarebbe se parlassi di scrivere due implementazioni di una stessa interfaccia, facendo in modo che una delle due lavori sull’input o sull’output dell’altra senza che il client dell’interfaccia lo sappia. Se parlo di object adapter chi mi ascolta sa di che cosa parlo, sa della necessità di implementare un’interfaccia estendendo una classe che già ne implementa, con interfaccia diversa, le responsabilità, e sa quali vantaggi ci sono rispetto all’adozione di un design a class adapter.

Un catalogo di design pattern è dunque, prima di tutto ed essenzialmente, un vocabolario comune, ogni elemento del quale facilita la comunicazione e la condivisione ed evoca – o dovrebbe evocare 😉 – una serie di dettagli mnemonici su contesto, pro e contro della soluzione nominata: dare un nome a qualcosa, in fondo, è il primo passo sulla strada che porta a controllarla.

Fotografare gli intenti

Il secondo motivo per cui penso sia importante, se non fondamentale, disporre di un framework concettuale come quello reso disponibile dalla conoscenza di un pattern consiste nel fatto che dare un nome ad una soluzione ne fotografa in qualche modo l’intento, aspetto importante almeno quanto il dettaglio della soluzione stessa.

Se poniamo l’accento su questo aspetto, infatti, ci rendiamo conto di come sia quasi immediato individuare fortissimi similitudini strutturali tra i design proposti da pattern diversi, benché l’intento di ciascuno di essi sia diverso.

STATE & STRATEGY

State e strategy, ad esempio, sono due pattern “gemelli”, identici nella struttura di design suggerita ma chiaramente differenti nell’intento: organizzare in classi diverse la gestione dei possibili stati di una FSM nel primo caso, supportare la possibilità di pluggare l’implementazione di un algoritmo nel secondo (cfr. [5]).

DECORATOR, PROXY & CHAIN OF RESPONSIBILITY

Allo stesso modo, decoratorproxy e chain of responsibility condividono la struttura (n implementazioni di una stessa interfaccia, ciascuna delle quali possiede un riferimento ad un’altra implementazione) ma la sfruttano per supportare intenti diversi: arricchire le funzionalità di un’implementazione base senza modificarla (decorator), incapsulare logiche di accesso condizionato ad un’implementazione esistente (proxy), gestire la scelta dell’handler giusto per una richiesta in modo OCP-compliant (chain of responsibility o varianti come set of responsibility).

BRIDGE & OBJECT ADAPTER

Ancora, object adapter e bridge condividono la struttura ma non l’intento: adattare un’implementazione esistente ad un’interfaccia nel primo caso, permettere ad interfaccia ed implementazione di variare indipendentemente nel secondo.

Ricapitolando:

  • un design pattern è una soluzione nota e condivisa ad un problema ricorrente
  • il design proposto da un pattern deriva da principi più astratti, l’applicazione dei quali porterebbe ad un design simile anche chi non fosse a conoscenza del pattern stesso
  • l’importanza di un catalogo di pattern risiede dunque nella definizione di un vocabolario condiviso, che facilita la comunicazione individuando per ogni pattern un intento, che lo differenzia rispetto ad altri pattern con struttura identica o simile


    Riferimenti
[1] – Software Dedign Patterns
[2] – Design Patterns, Elements of reusable object-oriented software
[3] – GRASP
[4] – SOLID principles
[5] – Head First, Design Patterns