1 Factory method
Scopo: creazionale
Raggio d’azione: basato su classi
Si può chiamare anche “Virtual Constructor”.
Definisce un’interfaccia per la creazione di un oggetto, lasciando alle sottoclassi la decisione sulla classe concreta che sarà istanziata.
Consente di deferire la creazione di un oggetto alle sottoclassi.
I framework utilizzano classi astratte per definire e mantenere le relazioni tra gli oggetti e spesso sono anche responsabili della creazione di questi oggetti.
Si consideri un framework per applicazioni in grado di presentare più documenti agli utenti.
La classe Application è responsabile della gestione di oggetti conformi all’interfaccia Document e della loro creazione. Application sa solo quando dovrà creare un nuovo documento ma non di che tipo né come dovrà farlo. La responsabilità è spostata all’esterno del framework: le sottoclassi di Application devono fornire l’implementazione del metodo factory createDocument() in modo da restituire un’istanza della classe appropriata.
Struttura
Product (Document): definisce l’interfaccia degli oggetti creati dal metodo factory.
ConcreteProduct (MyDocument): implementa l’interfaccia Product.
Creator (Application): dichiara il metodo factory, nell’esempio protetto, che restituisce un oggetto di tipo Product. Può invocare createProduct() per creare un prodotto.
ConcreteCreator (MyApplication): sovrascrive createProduct in modo da restituire una specifica istanza di ConcreteProduct.
Conseguenze
- Elimina la necessità di riferirsi a classi dipendenti dall’applicazione all’interno del codice del framework.
- Gli utilizzatori potrebbero essere costretti a definire sottoclassi di Creator per creare un particolare oggetto ConcreteProduct.
- Fornisce un punto di aggancio per le sottoclassi per la produzione di una versione specializzata di un prodotto.
- Connette gerarchie di classi parallele. Il metodo factory potrebbe essere invocato da oggetti diversi dal Creator, vedi esempio.
Gerarchie classi parallele
Figure è il tipo di base di figure geometriche manipolabili graficamente.
La logica di manipolazione può essere complicata e diversa da figura a figura.
Si può attribuire la responsabilità di implementare una logica di manipolazione ad oggetti separati introducendo il tipo Manipulator.
Si ottiene una gerarchia parallela a quella delle Figure. Ogni classe che implementa Figure ha il proprio metodo Figure, ha il proprio metodo factory per creare l’apposito Manipulator.
Pattern correlati: Abstract Factory.
3 Builder
Scopo: creazionale
Raggio d’azione: basato su oggetti
Separare la costruzione di un oggetto complesso dalla sua rappresentazione, in modo che lo stesso processo di costruzione possa essere utilizzato per creare rappresentazioni diverse. Riutilizzare lo stesso tipo di creazione per diverse rappresentazioni.
Struttura
BuilderIF: specifica l’interfaccia astratta che crea le parti dell’oggetto Product.
ConcreteBuilder: costruisce e assembla le parti del prodotto implementando l’interfaccia Builder; definisce e tiene traccia della rappresentazione che crea.
Director: costruisce l’oggetto utilizzando l’interfaccia Builder.
Product: rappresenta l’oggetto complesso e include le classi che definiscono le parti che lo compongono, includendo le interfacce per assemblare le parti nel risultato finale.
Interazioni
- Il client crea l’oggetto Director e lo configura tramite l’oggetto Builder desiderato.
- Il Director informa il Builder ogni volta che una parte di Product deve essere costruita.
- Il Builder riceve e gestisce le richieste del Director e aggiunge le parti al Product.
- Il client ottiene dal Builder il Product creato.
Conseguenze
- Consente di variare la rappresentazione interna di un prodotto. L’interfaccia del Builder consente di nascondere la rappresentazione, la struttura interna dell’oggetto Product e il processo di costruzione. Per modificare la rappresentazione del Product basta definire un nuovo tipo di Builder.
- Isola il codice per la costruzione e la rappresentazione. Migliora la modularizzazione incapsulando il modo di costruire e rappresentare un oggetto complesso. Per i client non è necessario conoscere le classi che definiscono la struttura interna del prodotto perché esse non appaiono nell’interfaccia Builder.
- Consente un migliore controllo del processo di produzione. A differenza di altri pattern creazionali che costruiscono il prodotto nella sua interezza, in questo caso il prodotto viene costruito passo dopo passo sotto il controllo del Director.
Costruttori telescopici
I metodi factory statici e i costruttori condividono una limitazione: non scalano bene quando ci sono molti parametri. Di solito la soluzione più adottata consiste nel fornire più costruttori con le varie combinazioni di parametri presenti. I costruttori telescopici funzionano ma il codice cliente risulta difficile da scrivere e ancora più difficile da leggere. Per capire che parametro è, bisogna calcolare la sua posizione.
Soluzione JavaBeans
Si introduce un costruttore senza parametri per creare un oggetto. I parametri richiesti si specificano invocando gli appositi metodi setter. Tuttavia siccome il processo di costruzione è diviso in varie chiamate l’oggetto creato potrebbe trovarsi in uno stato inconsistente. La classe non ha modo di verificare la consistenza dei parametri. Preclude la possibilità di rendere gli oggetti della classe immutabili.
La soluzione migliore consiste nel ricorrere all’uso del pattern Builder.
La classe Nutrition Facts definisce un builder come inner class statica. La classe Builder riproduce nei suoi campi quelli della classe NutritionFacts.
La classe Builder introduce un metodo per ciascun parametro opzionale. Questi metodi settano il parametro e restituiscono il builder stesso. Il metodo build crea l’oggetto fornendo atomicamente tutti i parametri per mezzo del builder.
Si noti che la classe NutritionFacts è immutabile. Ecco come appare il codice client:
NutritionFacts cocaCola = new NutritionFacts.Builder(240,8).calories(100).sodium(35).carbohydrate(27).build();
Questo codice è molto più facile da leggere e da scrivere.
Il Builder può imporre degli invarianti sui parametri durante l’invocazione di build(). Un singolo builder può essere utilizzato per creare più oggetti. Il builder può riempire alcuni campi automaticamente come ad esempio il numero seriale che si incrementa automaticamente ad ogni creazione.
Bridge
Scopo: strutturale
Raggio di azione: basato su oggetti
Disaccoppia un’astrazione dalla sua implementazione in modo che le due possano variare indipendentemente l’una dall’altra.
Altri nomi: Handle/Body
Quando un’astrazione può avere una tra più implementazioni possibili, in genere si risolve il problema ricorrendo all’ereditarietà.
L’astrazione viene definita da un’interfaccia o da una classe astratta e le sottoclassi concrete la implementano in modi differenti.
Tale approccio non è flessibile poiché l’ereditarietà lega un’implementazione ad un’astrazione in modo permanente. Ciò rende difficile modificare, estendere e riusare astrazioni ed implementazioni in modo indipendente.
Applicabilità
- Si vuole evitare un legame permanente tra un’astrazione e la sua implementazione, come nel caso in cui l’implementazione deve poter essere selezionata e/o modificata durante l’esecuzione.
- Si vuole avere la possibilità di estendere sia le astrazioni che le implementazioni per mezzo dell’ereditarietà. Tramite Bridge è possibile combinare le astrazioni e le implementazioni in vario modo e di estendere le une indipendentemente dalle altre.
- I cambiamenti nell’implementazione di un’astrazione non devono avere impatto sui client.
- Si vuole condividere una stessa implementazione fra più oggetti nascondendo questa condivisione ai client.
Struttura
Abstraction: specifica l’interfaccia dell’astrazione. Mantiene un riferimento ad un oggetto di tipo Implementor.
RefinedAbstraction: estende l’interfaccia definita da Abstraction.
Implementor: definisce l’interfaccia per le classi che implementano l’astrazione. Non deve corrispondere esattamente all’interfaccia di Abstraction: Implementor fornisce le operazioni base, mentre Abstraction definisce operazioni di più alto livello implementate sfruttando quelle di base.
ConcreteImplementor: definisce un’implementazione concreta dell’interfaccia Implementor.
Conseguenze
- Disaccoppiamento tra interfaccia e implementazione. Un’implementazione non è più legata in modo permanente a un’interfaccia. L’implementazione di un'astrazione può essere configurata durante l’esecuzione. Il disaccoppiamento tra Abstraction ed Implementor elimina la dipendenza dell’implementazione a tempo di compilazione. Ciò aiuta ad ottenere una struttura stratificata del sistema.
- Maggiore estensibilità. Le gerarchie di classi Abstract e Implementor possono essere estese indipendentemente.
- Mascheramento dei dettagli dell’implementazione ai client. I client non devono preoccuparsi dei dettagli implementativi, quali per esempio la condivisione di oggetti Implementor e il relativo meccanismo di conteggio dei riferimenti.
- Gestione delle risorse umane del progetto. Divisione di compiti: una parte si occupa dell’astrazione, l’altra della parte concreta. La modularità di questo pattern permette quindi di dividere il lavoro e quindi di lavorare in parallelo.
Pattern correlati: Abstract Factory può essere utilizzato per creare e configurare un particolare Bridge.
Adapter vs Bridge
Il pattern Adapter ha lo scopo di far cooperare tra di loro classi non correlate dopo che sono state progettate.
Il pattern Bridge è utilizzato all’inizio di un progetto per consentire ad astrazioni ed implementazioni di variare in modo indipendente.
8 Facade
Scopo: strutturale
Raggio d’azione: basato su oggetti
Fornire un’interfaccia unificata per un insieme di interfacce presenti in un sottosistema.
Facade definisce un’interfaccia di livello più alto che rende il sistema più semplice da utilizzare.
Struttura
Facade (Message Creator): conosce le classi nel sottosistema che sono responsabili di gestire una richiesta.
Classi del sottosistema (Message, MessageBody, Attachment): implementano le funzionalità del sottosistema. Non hanno alcuna conoscenza dell’esistenza del Facade: non hanno alcun riferimento ad esso.
Conseguenze
- Nasconde ai client i componenti del sottosistema, riducendo il numero degli oggetti con cui i client interagiscono.
- Promuove un basso accoppiamento tra un sottosistema ed i suoi client. I componenti del sottosistema sono molto accoppiati tra di loro (alta coesione interna). La riduzione delle dipendenze è di fondamentale importanza nei sistemi di grandi dimensioni.
- In particolare, riduce le dipendenze di compilazione: se le classi del sottosistema sono modificate non è necessario ricompilare il codice del client.
- Non impedisce alle applicazioni client di utilizzare le classi del sottosistema qualora sia necessario.
Pattern correlati: Abstract Factory, Mediator, Singleton.
9 Abstract factory
Scopo: creazionale
Raggio d’azione: basato su oggetti
Altri nomi: Kit
Consente di creare famiglie di oggetti correlati, fornisce una interfaccia comune senza specificare classi concrete.
Applicabilità
- Un sistema deve essere indipendente dalla modalità di creazione, composizione e rappresentazione dei suoi prodotti.
- Un sistema deve poter essere configurato scegliendo tra più famiglie di prodotti.
- Esistono famiglie di oggetti correlati, progettati per essere usati insieme.
- Si intende fornire una libreria di classi, rivelando le interfacce e nascondendo le implementazioni.
Struttura
AbstractFactory: dichiara un’interfaccia per le operazioni di creazione di oggetti prodotto astratti.
ConcreteFactory: implementa le operazioni degli oggetti prodotto concreti.
Product: dichiara un’interfaccia per un tipo di prodotti.
ConcreteProduct: implementa l’interfaccia Product definendo un oggetto prodotto creato dalla corrispondente factory concreta.
Conseguenze
- Isola le classi concrete. Poiché una classe factory incapsula il processo di creazione dei prodotti concreti rende i clienti indipendenti dalle classi utilizzate per la loro implementazione. I clienti conoscono solo le interfacce dei prodotti.
- Consente di cambiare in modo semplice la famiglia di prodotti utilizzata. È possibile ottenere una configurazione di oggetti diversi semplicemente cambiando il tipo di factory concreta utilizzata.
- Promuove la coerenza nell’utilizzo dei prodotti.
- L’aggiunta del supporto a nuove tipologie di prodotti è difficile. L’aggiunta di un nuovo tipo di prodotto comporta una modifica dell’interfaccia AbstractFactory e, di conseguenza, di tutte le classi che la implementano.
Implementazione
- Factory come Singleton. Un’applicazione tipicamente richiede una singola istanza di factory concreta per ciascuna famiglia di prodotti. Tale istanza è di solito ottenuta per mezzo del pattern Singleton.
- Creazione dei prodotti. Abstract Factory definisce un’interfaccia per la creazione di prodotti mentre la responsabilità della creazione effettiva delle istanze compete alle classi ConcreteProduct. Ciò può essere realizzato in vari modi:
- Introduzione di un metodo Factory per ogni prodotto.
- Ricorso al pattern Prototype: in questo caso la factory concreta viene inizializzata con un’istanza prototipo di ciascun prodotto della famiglia. La creazione dei prodotti avviene per clonazione dei prototipi.
11 Composite
Scopo: strutturale
Raggio d’azione: basato su oggetti
Comporre oggetti in strutture ad albero per rappresentare gerarchie parte-tutto e consentire ai client di trattare oggetti singoli e composizioni in modo uniforme.
Motivazione
Applicazioni quali editor grafici vettoriali o ambienti per la progettazione di circuiti consentono agli utenti di costruire diagrammi complessi a partire da semplici componenti.
Componenti semplici possono essere raggruppati per costruire componenti più complessi che possono a loro volta essere utilizzati come parti di componenti ancor più complessi.
Solitamente si introducono alcune classi per modellare gli oggetti semplici ed altre per rappresentare gli oggetti ottenuti per composizione.
Il pattern Composite introduce un’interfaccia comune per gli oggetti semplici e per quelli compositi in modo che il codice cliente li possa trattare uniformemente.
Struttura
Composite: dichiara l’interfaccia per gli oggetti componibili. Può essere una classe astratta che fornisce un’implementazione di default per i metodi di gestione dei componenti figli. Introduce (opzionale) un metodo per accedere al componente genitore.
Leaf: rappresenta gli oggetti primitivi i quali non hanno figli.
Composite: definisce il comportamento dei componenti che hanno figli. Memorizza i componenti figli e implementa i relativi metodi introdotti dall’interfaccia Component.
Conseguenze
- Definisce gerarchie di classi costituite da oggetti primitivi ed oggetti compositi. Gli oggetti primitivi possono essere composti per dare origine ad oggetti più complessi. Il codice cliente tratta oggetti compositi e primitivi in modo differente.
- Il codice cliente non sa e non è interessato a saperlo se sta utilizzando un oggetto primitivo o composito.
- Rende semplice l’aggiunta di nuovi tipi di componenti senza influenzare il codice cliente.
- L’interfaccia Component potrebbe essere troppo generale. Può essere necessario vincolare un particolare tipo di oggetto composito ad avere solo determinati tipi di componenti. Il compilatore non può garantire tale vincolo, per cui è necessario ricorrere a verifiche a tempo di esecuzione.
Implementazione
- Riferimenti al genitore. Fare in modo che ciascun componente conservi il riferimento al genitore può semplificare le operazioni di gestione e di visita della struttura composita. È importante fare in modo che tutti i figli di un oggetto composito riferiscano a esso stesso come genitore. Il modo più semplice per garantire ciò consiste nel modificare il riferimento al genitore contestualmente alle operazioni di aggiunta e rimozione dei figli.
- Massimizzazione dell’interfaccia Component. Al fine di rendere uniforme l’interfaccia dei componenti, Component potrebbe includere alcune operazioni che non hanno senso per i componenti primitivi. Ciò è in contrasto con il principio secondo cui una classe astratta dovrebbe includere solo le operazioni che hanno senso per le sottoclassi.
- Dichiarazione di metodi e gestione dei figli. Una scelta importante nell’implementazione del pattern riguarda il punto in cui sono definite le operazioni di gestione dei figli. Nell’esempio esse sono dichiarate da Component, un’alternativa sarebbe quella di introdurle nella classe composite.
- La prima scelta garantisce l’uniformità di interfaccia ma, di contro, se invocate su componenti primitivi tali operazioni falliscono o non hanno effetto.
- Nel secondo caso ci sono maggiori garanzie a tempo di compilazione ma si perde in termini di trasparenza.
13 Visitor
Scopo: comportamentale
Raggio d’azion
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
-
Schemi preparazione esame Matlab
-
Preparazione esame di Stato
-
Monografico Russo 2 - preparazione completa all'esame
-
Comunicazione Interculturale - preparazione completa all'esame