La conoscenza dei design pattern nello sviluppo di un software è fondamentale nel passaggio dalla fase di analisi alla progettazione del design strutturale.

Progettare software ad oggetti è difficile, ma lo è ancora di più renderne i suoi componenti riutilizzabili. La scomposizione dell’applicativo in oggetti deve tener conto di molteplici fattori: incapsulamento, granularità, dipendenza, performance, ridondanza, riusabilità, modularità, estendibilità nel tempo.

I design pattern vengono in nostro soccorso indicandoci soluzioni a problemi ricorrenti così da rendere il nostro codice riutilizzabile e mantenibile.

La genesi

Il concetto di design pattern è stato mutuato dall’ingegneria del software direttamente dall’architettura. Fu l’architetto austriaco Cristopher Alexander a formalizzare tale concetto nel 1977 all’interno del libro Pattern Language: Towns, Buildings, Construction.

Il pattern è una soluzione architetturale che può risolvere problemi in contesti eterogenei.

La trasposizione del concetto di design pattern in ambito informatico è attribuita invece alla cosiddetta Gang of Four (GoF) formata da Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides. Dopo quatro anni di confronto condensarono tutta la loro esperienza in un libro del 1994 che è diventato la “Bibbia della programmazione ad oggetti”: Design Patterns: elements of reusable object oriented software (Elementi per il riuso di software ad oggetti).

Il design pattern descrive un problema ricorrente e fornisce una soluzione che può essere riutilizzata infinite volte senza doverla applicare mai nello stesso identico modo

Design Pattern: classificazione

La GoF cataloga i suoi 23 pattern suddividendoli in 3 macro categorie:

  • Pattern creazionali: soluzioni per creare oggetti.
  • Pattern strutturali: soluzioni per la composizione strutturale di classi e oggetti.
  • Pattern comportamentali: soluzioni per gestire le responsabilità delle classi e degli oggetti.

Una seconda classificazione viene fatta in base al loro ambito applicativo:

  • Class pattern: soluzioni tramite classi e sottoclassi in relazioni statiche tra loro.
  • Object Pattern: soluzioni dinamiche basate sugli oggetti.

Infine ogni pattern viene descritto in base agli elementi che lo caratterizzano:

  • Nome/Alias: sintentizza l’essenza del pattern.
  • Scopo: cosa fa il pattern.
  • Motivazione: scenario del problema e soluzione offerta.
  • Applicabilità: situazioni di esempio in cui si può usare il pattern.
  • Struttura: rappresentazione grafica delle classi del pattern.
  • Partecipanti: classi/oggetti interessati e relative responsabilità
  • Collaborazioni: dei partecipanti per poter assumersi le responsabilità.
  • Conseguenze: pro e contro del pattern.
  • Implementazione: tecniche e consigli per implementare il pattern.
  • Codice d’esempio: in C++ e SmallTalk.
  • Utilizzi noti: esempi reali in sistemi esistenti.
  • Correlazioni: legami con altri pattern e differenze.

I 23 pattern definiti dal GoF possono essere quindi così schematizzati:

GoF Design Patterns

CREAZIONALI
(creational)
STRUTTURALI
(structural)
COMPORTAMENTALI
(behavioral)
Class Scope
Factory Method Adapter-class Interpreter
Template Method
Object Scope
Abstract Factory Adapter-object Chain of responsibility
Builder [] Bridge Command
Prototype Composite Iterator
Singleton [] Decorator Mediator
  Facade Memento
  Flyweight Observer
  Proxy State
    Strategy []
    Visitor

Design Pattern: il catalogo della GoF

Riporto qui una sintesi del catalogo composto dai 23 design pattern in modo da avere un’idea di massima del loro utilizzo.
Ricordo che con il termine “interfaccia” si intende qui l’insieme delle richieste a cui un oggetto può rispondere cioè l’insieme delle proprietà e dei metodi pubblici di un oggetto. Attenzione quindi a non confondersi con le “interface” di Php!

  • Abstract Factory: fornisce un’interfaccia per la creazione di famiglie di oggetti correlati o dipendenti senza specificare o conoscere le classi concrete.
  • Adapter: converte l’interfaccia di una classe in un’altra interfaccia richiesta dal client permettendo la collaborazione fra classi tra loro incompatibili.
  • Bridge: disaccoppia un’astrazione dalla sua implementazione in modo che le due possano variare indipendentemente.
  • Builder: separa la costruzione di un oggetto complesso dalla sua rappresentazione così che lo stesso processo di creazione possa creare rappresentazioni diverse.
  • Chain of Responsability: disaccoppia il mittente di una richiesta dal suo destinatario. Si ordinano gli oggetti destinatari e la richiesta viene passata di oggetto in oggetto finché non viene gestita.
  • Command: incapsula una richiesta in un oggetto.
  • Composite: raggruppa oggetti in strutture ad albero permettendo ai client di trattare oggetti singoli e raggruppamenti in modo uniforme.
  • Decorator: aggiunge dinamicamente responsabilità ad un oggetto. E’ un’aternativa flessibile all’ereditarietà.
  • Facade: definisce un’interfaccia di più alto livello per facilitare l’utilizzo di un sottosistema più complesso.
  • Factory Method: permette ad una classe di delegare la creazione di un oggetto alle sue sottoclassi.
  • Flyweight: usa la condivisione per gestire numerosi oggetti a granularità fine.
  • Interpreter: permette di implementare un proprio linguaggio con la sua grammatica ed il relativo interprete.
  • Iterator: permette di accedere agli elementi di un contenitore sequenzialmente senza esporre la struttura sottostante.
  • Mediator: favorisce un basso accoppiamento evitando che gli oggetti facciano riferimento l’un l’altro.
  • Memento: esplicita lo stato interno di un oggetto così da poterlo riportare al suo stato originale.
  • Observer: definisce una dipendenza uno-a-molti fra oggetti così che quando un oggetto cambia stato tutti gli oggetti dipendenti vengano notificati ed aggiornati.
  • Prototype: specifica gli oggetti da creare utilizzando un’istanza prototipale.
  • Proxy: fornisce un surrogato di un oggetto pper controllare l’accesso a quest’ultimo.
  • Singleton: forza una classe ad avere una sola istanza e ne fornisce un accesso globale.
  • State: permette ad un oggetto di modificare il proprio comportamento quando il suo stato interno cambia. Sembrerà che l’oggetto abbia cambiato la sua classe.
  • Strategy: definisce una famiglia di algoritmi, li incapsula e li rende interscambiabili tra di loro.
  • Template Method: definisce lo scheletro principale dell’algoritmo in un metodo delegando alcuni passi alle sottoclassi.
  • Visitor: permette di definire nuove operazioni senza cambiare le classi degli elementi su cui opera.

Riferimenti

Design Patterns: Elements of Reusable Object-Oriented Software
Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
Pearson Education

https://designpatternsphp.readthedocs.io

https://refactoring.guru/design-patterns