I Design Pattern sono metodi riutilizzabili per risolvere problemi di progettazione comuni all’interno di un determinato contesto. In altre parole offrono una soluzione progettuale generale a problemi ricorrenti. tali modelli sono formalizzati come best practice che lo sviluppatore può utilizzare per risolvere problemi comuni durante la progettazione dell’applicazione. Ciò non significa che debbano essere sempre applicati ma quando e come applicarli in una situazione e un progetto specifico dipende dai criteri adottati dallo sviluppatore .
L’utilizzo dei Design Pattern comunque ha l’indubbio vantaggio di migliorare notevolmente il processo di sviluppo e le sue prestazioni. Una efficace progettazione richiede la considerazione di problemi che potrebbero non essere visti prima dell’implementazione. Riutilizzare modelli di progettazione noti aiuta a prevenire problemi che possono causare danni importanti nelle fasi successive di sviluppo e migliorano la leggibilità del codice per gli sviluppatori che hanno familiarità con i Design Pattern.
Sebbene il termine fosse già stato introdotto da Christopher Alexander in architettura, il successo dei Design Pattern nell’ambito dell’ingegneria del software si deve principalmente al celebre libro Design Patterns Elements of Reusable Object-Oriented Software del 1995 i cui autori sono Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides, e che la comunità scientifica chiama, per brevità, la “banda dei quattro” o Gang of Four o Gof.
I design pattern possono essere classificati con diversi criteri, i più comuni dei quali sono quelli che evidenziano il tipo di problema che si cerca di risolvere.
Creational Pattern
Tali pattern trattano le modalità con cui è possibile istanziare una classi. Questi modelli possono poi essere ulteriormente suddivisi in class-creation pattern e object-creational pattern. Mentre i primo utilizzano l’ereditarietà nel processo di creazione delle istanze di una classe, i secondi utilizzano il concetto di delega per eseguire l’attività di creazione di un oggetto.
Abstract Factory | Offre l’interfaccia per la creazione di una famiglia di oggetti correlati, senza specificare esplicitamente le loro classi. |
Builder | Definisce un’istanza per la creazione di un oggetto ma lascia che le sottoclassi decidano quale classe istanziare e consente un controllo più preciso sul processo di costruzione. |
Factory Method | Definisce un’interfaccia per la creazione di oggetti, ma lascia che le sottoclassi decidano quale classe istanziare e si riferisce all’oggetto appena creato attraverso un’interfaccia comune. |
Object Pool | Evita la creazione di oggetti che richiedono parecchie risorse riutilizzando e condividendo oggetti non più utilizzati. |
Singleton | Assicura che venga creata una sola istanza di una classe e che fornisca un punto di accesso globale all’oggetto. |
Prototype | Specifica i tipi di oggetti da creare utilizzando un’istanza prototipo e crea nuovi oggetti copiando il prototipo. |
Structural Pattern
Tali pattern si occupano di come classi e oggetti vengono composti. I pattern structural class-creation utilizzano l’ereditarietà per comporre interfacce, mente i pattern structural object-patterns definiscono i modi in cui comporre oggetti per ottenere nuove funzionalità.
Adapter | Converte l’interfaccia di una classe in un’altra interfaccia che i client si aspettano. Tale pattern consente a classi con interfacce incompatibili (perchè diverse) di lavorare insieme. |
Bridge | Consente di separare l’interfaccia di un oggetto dalla sua implementazione in modo che i due possano variare indipendentemente. |
Composite | Consente di comporre oggetti in strutture ad albero per rappresentare gerarchie intere o parti di esse, permettendo ai client di trattare in modo uniforme singoli oggetti e composizioni di oggetti. |
Decorator | Aggiungi in modo dinamico responsabilità aggiuntive ad un oggetto. |
Facade | Consente attraverso un’interfaccia più semplice di accedere a sottosistemi che espongono interfacce complesse e molto diverse tra loro. |
Flyweight | Utilizza la condivisione per supportare un numero elevato di oggetti che hanno in comune parte del loro stato interno in cui l’altra parte di stato può variare. |
Proxy | Fornire un placeholder per un oggetto al fine di controllare i riferimenti ad esso. |
Behavioral Pattern
Tali pattern si occupano di identificare schemi ricorrenti di comunicazione tra gli oggetti al fine di aumentare la flessibilità nello svolgimento di questa operazione.
Chain of Responsibiliy | Evita di connettere il mittente di una richiesta al suo ricevitore, dando così la possibilità ad altri oggetti di gestire anche la richiesta. Gli oggetti diventano parti di una catena e la richiesta viene inviata da un oggetto all’altro risalendo la catena finché uno degli oggetti non la gestirà. |
Command | Incapsula una richiesta in un oggetto, consentendo la parametrizzazione dei client con richieste diverse e consentendo di salvare le richieste in una coda. |
Interpreter | Dato un linguaggio, definisci una rappresentazione per la sua grammatica e un interprete che usa la rappresentazione per interpretare le frasi nella lingua. |
Iterator | Fornisce un modo per accedere in modo sequenziale agli elementi di un oggetto aggregato senza esporre la sua rappresentazione interna. |
Mediator | Definisce un oggetto che incapsula il modo in cui un insieme di oggetti interagisce semplificandone la comunicazione. Il mediator promuove il loose coupling impedendo esplicitamente agli oggetti di riferirsi l’un l’altro e consentendo di variare la loro interazione in modo indipendente. |
Memento | Cattura lo stato interno di un oggetto, senza violarne l’incapsulamento, per memorizzarlo e per poterlo poi ripristinare in un momento successivo. |
Null Object | Fornisce un oggetto che è un surrogato per la mancanza di un elemento di un determinato tipo, agendo come valore predefinito. Tale pattern definisce in modo intelligente il comportamento di “non fare nulla”, nascondendo i dettagli ai suoi collaboratori. |
Strategy | Definire una famiglia di algoritmi che vengono incapsulati e resi intercambiabili in modo intercambiabile. Tale pattern consente all’algoritmo di variare in modo indipendente dai client che lo utilizzano. |
State | Consente ad un oggetto di modificare il proprio comportamento in seguito ad un cambiamento nel proprio stato interno. Questo pattern è molto vicino al concetto di macchine a stati finiti e può essere interpretato come uno strategy pattern, in cui la strategia è cambiata a seguito dell’invocazione di metodi definiti dell’interfaccia del modello. |
Template Method | Consente di definire la struttura dell’algoritmo utilizzato in un’operazione, rinviando alcuni passaggi alle sottoclassi. In altre parole consente alle sottoclassi di ridefinire determinati step di un algoritmo senza consentire loro di modificare la struttura dell’algoritmo stesso. |
Visitor | Rappresenta un’operazione da eseguire sugli elementi di una struttura di oggetto. Visitatore consente di definire una nuova operazione senza modificare le classi degli elementi su cui opera. |
Architectural Pattern
Hanno un’applicazione più ampia rispetto ai pattern precedenti e sono ampiamente utilizzati nei più famosi framework attuali.
Front Controller | Utilizzato principalmente in applicazioni web, fornisce un punto di ingresso centralizzato per la gestione delle richieste. |
MVC | Il pattern Model-View-Controller, dividere un’applicazione in tre parti interconnesse, separando la logica di presentazione dei dati e dalla logica di business. |
ADR | Il pattern Action-Domain-Responser è proposto come un pattern MVC più raffinato e orientato allo sviluppo web. |
Service Locator | Questo pattern implementa un layer di astrazione che incapsula il processo utilizzato per ottenere le informazioni necessarie a contattare un determinato servizio. Sostanzialmente utilizza un registro centralizzato, detto service locator, per conservare tali informazioni e restituirle al richiedente. |
Active Record | E’ un pattern che consente l’accesso agli elementi di un database attraverso una astrazione del modello relazionale. |
Publish/Subscribe | E’ un pattern utilizzato per consentire una comunicazione asincrona tra diversi processi, oggetti o atre tipologie di agenti software, al fine di notificare i cambiamenti interni di un oggetto a tutti gli oggetti che si sono sottoscritti in modo rapido ed efficace. |
Inversion of Control (IoC) | Con tale pattern si intende tener disaccoppiati i singoli componenti di un sistema ed in cui le eventuali dipendenze non vengono scritte all’interno del componente stesso, ma gli vengono iniettate dall’esterno. Gli oggetti quindi non istanziano e richiamano gli oggetti dal quale il loro lavoro dipende, ma queste funzionalità vengono fornite da un ambiente esterno tramite dei contratti definiti da entrambe le entità. |
Altri Pattern
Questi pattern non sono classificabili in alcuna delle categorie descritte sopra, ma sono comunque ampiamente utilizzati nei framework.
Dependency Injection (DI) | Questo pattern implementa IoC per risolvere le dipendenze tra gli oggetti. |
Lazy Loading | E’ un pattern in cui l’inizializzazione di un oggetto, che può essere una operazione onerosa, è differita fino a quando l’oggetto è effettivamente utilizzato. |
Mock Object | E’ un pattern che prevede di simulare in modo controllato il comportamento di un oggetto. E’ utilizzato appositamente per la realizzazione dei test. |
Table Data Gateway | In tale pattern un oggetto agisce come gateway per l’accesso ad un database. L’idea è quella di separare la responsabilità del recupero degli oggetti dal DB dal loro effettivo utilizzo. |