Corso di Laurea Specialistica in Ingegneria Informatica Design Pattern E. TINELLI Corso di Ingegneria del Software A. A. 2008 - 2009 Pattern e Design Pattern • Soluzioni consolidate a problemi ricorrenti in contesti specifici – Catturano soluzioni che sono già state adottare e che si sono evolute nel tempo – Catturano le strutture statiche e dinamiche di soluzioni che ricorrono più volte durante lo sviluppo di applicazioni in un particolare contesto • Un pattern deve mostrare che la soluzione descritta è: Utile (ricorrenza); Utilizzabile (contesto specifico); Usata (assodata) • Goal dei Design Pattern – Creare un linguaggio per comunicare intuizioni ed esperienza su problemi e sulle loro soluzioni Æ Aiutare gli sviluppatori a risolvere problemi già trattati – Offrire soluzioni generiche, riusabili, documentate e al tempo stesso specifiche per una certa classi di problemi “Ogni “Ognipattern patterndescrive descriveun unproblema problemaspecifico specificoche chericorre ricorrepiù piùvolte volte nel nelnostro nostroambiente ambienteeepoi poidescrive descriveililnucleo nucleodella dellasoluzione soluzioneaa quel quelproblema, problema,ininmodo mododa dapoter poterutilizzare utilizzaretale talesoluzione soluzione un unmilione milionedidivolte, volte,senza senzamai maifarlo farloallo allostesso stessomodo”. modo”. C. C. Alexander Alexander et et al. al. AA Pattern Pattern Language, Language, 1977 1977 E. TINELLI – Ingegneria del Software A. A. 20082008-2009 2 Ruolo dei Design Pattern • Identificare gli oggetti necessari: scomporre sistema in oggetti è compito difficile. Pattern aiutano nell’identificazione di oggetti che rappresentano astrazioni non banali a partire dalla definizione del problema che si vuole risolvere. • Progettare anticipando i cambiamenti: Progettare il Sistema in modo da anticipare i nuovi requisiti e i cambiamenti a quelli esistenti Æ Non esiste una combinazione di Patterns che dia il massimo di flessibilità e di facilità di evolvere. • Controllare le dipendenze: Le dipendenze contenute in una classe/interfaccia consistono nell’insieme dei riferimenti (statici) ad altre classi/interfacce Æ I design pattern vengono classificati in base al tipo di dipendenze su cui agiscono in: creazionali, strutturali,comportamentali • Definizione delle interfacce: l’applicazione di un pattern porta con se le scelte riguardanti le interfacce ed i meccanismi di interazione tra gli oggetti. • Definire l’implementazione: applicare un pattern comporta specifiche scelte implementative. In particolare vi sono pattern che si focalizzano su ereditarietà mentre altri si focalizzano su meccanismi di composizione. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 3 Pattern – 4 elementi essenziali • Nome - riferimento mnemonico che permette di aumentare il vocabolario dei termini tecnici e ci permette di identificare il problema e la soluzione in una o due parole • Problema - descrizione del problema e del contesto a cui il pattern intende fornire una soluzione. Potrebbe anche essere descritto come una lista di condizioni necessarie per l’applicazione del pattern • Soluzione - descrive gli elementi fondamentali che costituiscono la soluzione e le relazioni che intercorrono tra questi mediante uno schema generale che può essere applicato in situazioni diverse • Conseguenze - specifica le possibili conseguenze che l’applicazione della soluzione proposta può comportare. Si riferiscono ad esempio a possibili di efficienza della soluzione, oppure ad applicabilità con specifici linguaggi di programmazione Æ utili per valutare soluzioni alternative E. TINELLI – Ingegneria del Software A. A. 20082008-2009 4 Pattern per la progettazione OO – principi e tecniche • Ereditarietà di Interfaccia – l’ereditarietà di classi concrete permette il riuso di codice mentre l’ereditarietà di classi puramente astratte permette di definire oggetti con la stessa interfaccia Æ 1° Tecnica: Programmare basandosi su un’interfaccia e non su un’implementazione • Esistono due possibilità per il riuso delle funzionalità (quindi di codice): – white-box reuse (basato sull’ereditarietà) • È semplice adattare le funzionalità di una classe antenata in una classe derivata • L’ereditarietà è definita in maniera statica, al compile-time • Si viola parzialmente l’incapsulamento – black-box reuse (basato sull’aggregazione/composizione) • La composizione è definita dinamicamente, a run-time • Gli oggetti contenuti devono avere interfacce ben definite e complete per poter essere utilizzati correttamente • Si preserva l’incapsulamento Æ 2° Tecnica: Preferire l’aggregazione all’ereditarietà E. TINELLI – Ingegneria del Software A. A. 20082008-2009 5 Pattern Creazionali • Astraggono il processo di istanziazione – Nascondono i costruttori e introduco dei metodi al loro posto – Tutto ciò che il sistema conosce sono le interfacce degli oggetti così come sono state definite con opportune classi astratte • Conseguenze: – Incapsulano la conoscenza delle classi concrete usate – Nascondono il modo in cui le istanze di queste classi vengono create e assemblate – Espongono interfacce al posto di tipi concreti • Esempi – Abstract Factory – Factory Method – Builder – separa la costruzione di un oggetto complesso dalla sua rappresentazione in modo che lo stesso processo di costruzione possa essere utilizzato per creare rappresentazioni diverse – Singleton – fa in modo che una classe abbia una sola istanza e fornisce un punto di accesso noto a tutti gli utilizzatori E. TINELLI – Ingegneria del Software A. A. 20082008-2009 6 Pattern Strutturali • Descrivono come comporre classi e oggetti in strutture più grandi – Si dividono in: basati su classi e su oggetti – I primi usano l’ereditarietà (statica) – I secondi la composizione (anche dinamica) • Conseguenze – Aggiungono un livello di indirezione per disaccoppiare le due interfacce. • Esempi – – – – – Adapter Composite Decorator Facade Proxy – fornisce un surrogato di un oggetto per controllare l’accesso a quest’ultimo E. TINELLI – Ingegneria del Software A. A. 20082008-2009 7 Pattern Comportamentali • Riguardano algoritmi e l’assegnamento di responsabilità tra oggetti – Descrivono pattern di comunicazione tra oggetti. – Per distribuire il comportamento usano l’ereditarietà o la composizione. • Conseguenze – Alcuni incapsulano il comportamento in modo da poterlo variare anche dinamicamente – Disaccoppiano il chiamante e il chiamato aggiungendo un livello di indirezione. • Esempi – Iterator– fornisce un modo per accedere agli elementi di un contenitore (un oggetto aggregato) sequenzialmente senza esporre la struttura dati sottostante – Mediator – definisce un oggetto che incapsula le strategie di collaborazione di un gruppo di oggetti evitando che gli oggetti facciano riferimento l’uno all’altro esplicitamente. – Observer – Strategy – Visitor – rappresenta un’operazione da svolgersi sugli elementi di una struttura di oggetti. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 8 Un possibile catalogo di Design Pattern (da Design Patterns – elementi per il riuso di software a oggetti) E. TINELLI – Ingegneria del Software A. A. 20082008-2009 9 Relazioni tra Patterns (da Design Patterns – elementi per il riuso di software a oggetti) E. TINELLI – Ingegneria del Software A. A. 20082008-2009 10 Design pattern in dettaglio ••Creazionali Creazionali ••Factory FactoryMethod Method ••Abstract AbstractFactory Factory ••Strutturali Strutturali ••Adapter Adapter ••Composite Composite ••Decorator Decorator ••Facade Facade ••Comportamentali Comportamentali ••Observer Observer ••Strategy Strategy E. TINELLI – Ingegneria del Software A. A. 20082008-2009 11 Abstract Factory • Scopo: definire un’interfaccia per la creazione di famiglie di oggetti correlati o dipendenti senza specificare quali siano le classi concrete. • Motivazione: spesso ci si trova di fronte al problema di voler istanziare un oggetto senza specificare precisamente la classe. Comportamento chiaramente non possibile con meccanismi di creazione tipo new. • Applicabilità: utilizzato quando: – sistema indipendente dalle modalità di creazione, composizione e rappresentazione dei suoi prodotti; – sistema configurabile dipendentemente dalle caratteristiche di una tra piú tipologie di oggetto – Si vuole fornire una libreria di classi rivelando solo interfaccia ma non implementazioni specifiche E. TINELLI – Ingegneria del Software A. A. 20082008-2009 12 Abstract Factory – Struttura E. TINELLI – Ingegneria del Software A. A. 20082008-2009 13 Abstract Factory – Partecipanti • AbstractFactory: – Dichiara una interfaccia per le operazioni che creano e restituiscono i prodotti. – Nella dichiarazione di ogni metodo, i prodotti restituiti sono dei tipi AbstractProduct. • ConcreteFactory: – Implementa l’AbstractFactory, fornendo le operazioni che creano e restituiscono oggetti corrispondenti a prodotti specifici (ConcreteProduct). • AbstractProduct: – Dichiarano le operazioni che caratterizzano i diversi tipi generici di prodotti. • ConcreteProduct: – Definiscono i prodotti creati da ogni ConcreteFactory. • Client: – Utilizza l’AbstractFactory per rivolgersi alla ConcreteFactory di una famiglia di prodotti. – Utilizza i prodotti tramite la loro interfaccia AbstractProduct. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 14 Abstract Factory – Struttura di Esempio • • • Si pensi ad un dimostratore dell’utilizzo di due famiglie di prodotti, basate su tecnologie diverse: nastro (tape) e CD. Ogni famiglia è composta dal supporto stesso (tape o cd), un masterizzatore (recorder) e un riproduttore (player). Si vuole che un cliente sia in grado di eseguire lo stesso processo di prova su prodotti di entrambe le famiglie. Si deve definire un modo per creare famiglie complete di prodotti, senza vincolare alla codifica del cliente che gli utilizza, il codice delle particolari famiglie. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 15 Abstract e Concrete Factory public interface DevicesFactory { } public Player createPlayer(); public Recorder createRecorder(); public Media createMedia(); public class CDDevicesFactory implements DevicesFactory { public Player createPlayer() { return new CDPlayer(); } public Recorder createRecorder() { return new CDRecorder(); } public Media createMedia() { } } return new CD(); né l’AbstractFactory né gli AbstractProduct implementano operazioni Æ in Java diventa più adeguato codificarli come interfacce piuttosto che come classi astratte. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 16 Classi appartenenti alla famiglia di prodotti CD: CD, CDPlayer e CDRecorder public class CD implements Media{ } private String track = ""; public void writeOnDisk( String sound ) { track = sound; } public class CDPlayer implements Player { public String readDisk( ) { CD cDInside; return track; public void accept( Media med ) { } cDInside = (CD) med; } public void play( ) { if( cDInside == null ) System.out.println( "Error: No CD." ); else System.out.println( cDInside.readDisk() ); } } E. TINELLI – Ingegneria del Software A. A. 20082008-2009 17 Client class Client { DevicesFactory technology; public void selectTechnology( DevicesFactory df ) { technology = df; } public void test(String song) { Media media = technology.createMedia(); Recorder recorder = technology.createRecorder(); Player player = technology.createPlayer(); } recorder.accept( media ); recorder.record( song ); player.accept( media ); player.play(); } E. TINELLI – Ingegneria del Software A. A. 20082008-2009 18 Istanza di un oggetto Client public class AbstractFactoryExample { public static void main ( String[] arg ) { Client client = new Client(); System.out.println( “**Testing CD devices” ); client.selectTechnology( new CDDevicesFactory() ); client.test( “Moon river" ); } } E. TINELLI – Ingegneria del Software A. A. 20082008-2009 19 Abstract Factory - Conseguenze • Isola le classi concrete - i clienti non devono sapere niente delle classi concrete che useranno, neanche al momento della creazione • Grazie alle factory è possibile racchiudere la logica di creazione in un unico modulo, delimitandola con precisione e permettendo così, nel resto del programma, di stabilire effettivamente tra moduli dipendenze basate unicamente su interfacce. • È facile cambiare famiglia di prodotto basta cambiare la linea di codice che riguarda la creazione della factory • Promuove la consistenza tra i prodotti poiché i prodotti sono organizzati in famiglie. I prodotti di una famiglia sono coordinati per lavorare insieme • Supportare l’inserimento di un nuovo prodotto in una famiglia è difficile poiché richiede cambiamenti all’Abstract Factory ed a tutte le sue sottoclassi • La creazione di oggetti non avviene nel modo standard Æ i clienti devono sapere che devono usare la factory invece del costruttore per gli oggetti E. TINELLI – Ingegneria del Software A. A. 20082008-2009 20 Factory Method • Definisce un’interfaccia per creare un oggetto, ma sono le sottoclassi che decidono quale classe istanziare Æ Factory Method permette ad una classe di delegare l’istanziazione alle sue sottoclassi • Il pattern Factory Method incapsula la conoscenza di quali classi occorre creare: – Permette di scrivere una classe che delega la creazione degli oggetti che usa, rendendola indipendente dalle rispettive classi • Usare il pattern Factory Method quando – Una classe non è in grado di anticipare quale classe di oggetti deve creare – È desiderabile centralizzare la fase di creazione degli oggetti in una classe dedicata (es. creazione di diversi tipi di documenti all’interno di un’applicazione da ufficio quali documenti testuali, fogli di stile, ecc.) – Il sistema usa classi astratte per definire e gestire relazioni tra oggetti – Il sistema deve anche creare oggetti: deve istanziare le classi ma conosce solo le classi astratte, che non può istanziare E. TINELLI – Ingegneria del Software A. A. 20082008-2009 21 Factory Method – Struttura e Partecipanti • Product - Definisce l’interfaccia degli oggetti creati dal Factory Method • ConcreteProduct - Implementa l’interfaccia del prodotto Product • Creator – Dichiara il Factory Method che restituisce l’oggetto di tipo Prodotto – Può contenere un’implementazione di default del Factory Method – Creator si basa sulle sue sottoclassi per definire il Factory Method in modo che restituisca un’istanza del Concrete Product appropriato • ConcreteCreator - Ridefinisce (override) il Factory Method per restituire un’istanza di ConcreteProduct E. TINELLI – Ingegneria del Software A. A. 20082008-2009 22 Factory Method – Esempio • Sistema per la manipolazione di elementi cartografici. • Due classi astratte: – classe Elemento che rappresenta qualunque tipo di oggetto da posizionare in una mappa – classe Strumento che fornisce le operazioni comuni di manipolazione degli Elementi. • Il sistema sa quando un particolare tipo di elemento deve essere creato (ad es., dopo aver richiesto un identificativo per un nuovo elemento), ma non il tipo particolare di Elemento a creare. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 23 Factory Method - Conseguenze • La classe che richiede la creazione è indipendente dalle classi degli oggetti “concrete product” che utilizza • Utilizzare un metodo factory per la creazione di oggetti di una classe fornisce sempre una flessibilità maggiore rispetto alla creazione diretta dell’oggetto – È possibile usare il pattern Factory Method come metodo standard per la creazione di oggetti? • L’insieme degli oggetti “concrete product” che possono essere creati può cambiare dinamicamente E. TINELLI – Ingegneria del Software A. A. 20082008-2009 24 Adapter • Utile quando bisogna connettere diversi elementi eterogenei poichè adatta l’interfaccia di un elemento di un sistema (adaptee) ad una forma richiesta da uno dei suoi client • Converte l’interfaccia di una classe in un’altra interfaccia richiesta dal client Æ consente a classi diverse di operare insieme quando ciò non sarebbe altrimenti possibile a causa di interfacce incompatibili (es. il client è .NET mentre l’adaptee è Java) • È un“adattatore” che separa client e adaptee: – Comunicazioni tra client e adaptee sono gestite dall’adapter – il ruolo dell’adattatore è semplicemente di interpretare le richieste del client, trasformarle in richieste all’adaptee, ottenere risposte dall’adaptee, trasformarle in risposte al client – l’adattatore ha in generale un’interfaccia (target) diversa da quella dell’adaptee E. TINELLI – Ingegneria del Software A. A. 20082008-2009 25 Adapter – Struttura Ereditarietà Multipla Composizione di oggetti E. TINELLI – Ingegneria del Software A. A. 20082008-2009 26 Adapter – Esempio public class RectangleObjectAdapter implements Polygon{ Rectangle adaptee; private String name = "NO NAME"; public RectangleObjectAdapter() { adaptee = new Rectangle(); } public void define( float x0, float y0, float x1, float y1,String col ){ float a = x1 - x0; float l = y1 - y0; adaptee.setShape( x0, y0, a, l, col); } public float getSurface() { return adaptee.getArea(); } public float[] getCoordinates() { float aux[] = new float[4]; aux[0] = adaptee.getOriginX(); aux[1] = adaptee.getOriginY(); aux[2] = adaptee.getOppositeCornerX(); aux[3] = adaptee.getOppositeCornerY(); return aux; } public void setId( String id ) { name = id; } E. TINELLI – Ingegneria del Software A. A. 20082008-2009 27 Adapter - Conseguenze • disaccoppiamento delle implementazioni del client e dell’adaptee – l’implementazione di ciascun elemento può variare senza che questo si ripercuota sull’altro elemento • l’adaptee può essere usato da diversi tipi di client ciascuno col suo adattatore • l’indirezione addizionale potrebbe ridurre l’efficienza – ci potrebbe essere un aumento nell’overhead per la manutenzione – se cambia il servizio offerto dall’adaptee – e quindi oltre all’adaptee (ed eventualmente al client) deve cambiare anche l’adattatore E. TINELLI – Ingegneria del Software A. A. 20082008-2009 28 Composite • Compone oggetti in strutture ad albero per rappresentare gerarchie di componenti – Oggetti singoli e composti vengono trattati uniformemente – Si possono aggiungere altri componenti alla gerarchia senza influenzare gli altri • Consente la costruzione di gerarchie di oggetti composti. Gli oggetti composti possono essere formati da oggetti singoli, oppure da altri oggetti composti. • Questo pattern è utile nei casi in cui si vuole: – Rappresentare gerarchie di oggetti tutto-parte. – Essere in grado di ignorare le differenze tra oggetti singoli e oggetti composti. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 29 Composite – Struttura e Partecipanti • Component: – – • Leaf: – – • Estende la classe Component, per rapperesentare gli oggetti che non sono composti (foglie). Implementa le operazioni per questi oggetti. Composite: – – – • Dichiara una interfaccia comune per oggetti singoli e composti. Implementa le operazioni di default o comuni tutte le classi. Estende la classe Component, per rappresentare gli oggetti che sono composti. Immagazzina al suo interno i propri componenti. Implementa le operazioni proprie degli oggetti composti, e particolarmente quelle che riguardano la gestione dei propri componenti. Client: Utilizza gli oggetti singoli e composti tramite l’interfaccia rappresentata dalla classe astratta Component. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 30 Composite – Esempio 1 E. TINELLI – Ingegneria del Software A. A. 20082008-2009 31 Composite – Esempio 2 • • • • • Consideriamo l’organizzazione di un documento suddiviso in capitoli e paragrafi rappresentati da altrettanti oggetti il cui tipo base è la classe astratta DocumentElement Ciascun capitolo (classe DocumentChapter) è un elemento composito che contiene uno o più paragrafi. L’elemento “foglia” della struttura del documento è rappresentato dal paragrafo (classe DocumentParagraph). L’implementazione interna dell’elemento “paragrafo” è differente da quella dell’elemento composito (il capitolo). Si noti inoltre che nulla vieta che un capitolo possa a sua volta contenere uno o più sottocapitoli sempre di tipo DocumentChapter. Si noti infine come l’aggiunta di una nuova tipologia di elemento (per esempio, una ipotetica classe DocumentSection) non rappresenti un grosso problema, dal momento che la struttura è pensata per evolvere in modo flessibile nel tempo. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 32 Composite - Conseguenze • Definisce gerarchie di classi costituite da oggetti primitivi e composti in modo del tutto trasparente al client che potrà utilizzarli indifferentemente • Semplifica il client: non è necessario scrivere “blocchi case” per distinguere tra oggetti semplici e composti poiché tutti gli oggetti hanno un’interfaccia uniforme • Rende più semplice l’aggiunta di nuove tipologie di componenti • Può rendere il progetto troppo generico – se si vuole che la struttura composita contenga solo alcuni componenti? E. TINELLI – Ingegneria del Software A. A. 20082008-2009 33 Decorator • Aggiunge dinamicamente responsabilità addizionali ad un oggetto. In questo modo si possono estendere le funzionalità d’oggetti particolari senza coinvolgere complete classi. • Per assegnare responsabilità a singoli oggetti, in maniera dinamica e trasparente (cioè senza coinvolgere altri oggetti) • Utile quando l’estensione attraverso la specializzazione è sconveniente. • Esempio: vogliamo che una text box abbia anche una scrollbar • Problema: vogliamo che questa estensione venga decisa dinamicamente, durante l’esecuzione Æ non si vuole quindi ricorrere alla specializzazione • Soluzione: – incapsulare l’oggetto principale (nel nostro esempio, la text box) in un altro oggetto chiamato decorator (nel nostro esempio, la scrollbar) – Il decorator deve fornire servizi conformi con quelli forniti dall’oggetto principale E. TINELLI – Ingegneria del Software A. A. 20082008-2009 34 Decorator – Struttura e Partecipanti • Component: Specifica l’interfaccia degli oggetti che possono avere delle responsabilità aggiunte dinamicamente. • ConcreteComponent: Implementa l’oggetto in cui si possono aggiungere nuove responsabilità. • Decorator: Possiede un riferimento all’oggetto Component e specifica un’interfaccia concordante con l’interfaccia Component. • ConcreteDecorator: Aggiunge nuove responsabilità al Component. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 35 Decorator – Esempio • Esiste un modello di oggetti che rappresenta gli impiegati (Employee) di una azienda. • Il sistema comprende la possibilità di investire gli impiegati con delle responsabilità aggiuntive. • È necessario definire un modo per aggiungere dinamicamente nuove responsabilità ad oggetto specifico. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 36 Decorator – Esempio abstract class ResponsibleWorker implements Employee { protected Employee responsible; public ResponsibleWorker(Employee employee) { responsible = employee; } public String getName() { return responsible.getName(); } public String getOffice() { return responsible.getOffice(); } public void whoIs() { responsible.whoIs(); } } E. TINELLI – Ingegneria del Software A. A. 20082008-2009 37 Decorator – Esempio public class ProjectManager extends ResponsibleWorker { private String project; public ProjectManager( Employee empl, String proj ) { super( empl ); project = proj; public class DecoratorExample1 { } public static void main(String arg[]) { public void whoIs() { Employee one = new Engineer( “John Doe", "Programming Department" ); super.whoIs(); System.out.println( "I am the one = new AdministrativeManager(one); Manager of the Project:" + project ); } one = new ProjectManager(one, } “Project1" ); one = new ProjectManager( one, “Project2" ); } } E. TINELLI – Ingegneria del Software A. A. 20082008-2009 38 Decorator - Conseguenze • Maggiore flessibilità dell’ereditarietà – Il pattern Decorator fornisce un sistema più flessibile di quello che si otterrebbe con l’ereditarietà (eventualmente multipla). – Con il Decorator le responsabilità possono essere aggiunte e rimosse a run-time (mutando dei riferimenti). • Funzionalità complesse ottenute mediante aggregazione di piccoli semplici pezzi • Le funzionalità possono essere attivate in maniera mirata. Ciò evita di avere un’unica classe che prevede tutte le possibili estensioni (complessa e inefficiente) • Eccesso di piccole classi simili tra di loro – Ciò rende il codice più complesso da comprendere e da manutenere per chi non conosce bene le varie classi Decorator E. TINELLI – Ingegneria del Software A. A. 20082008-2009 39 Facade • Obiettivi – Fornire un’interfaccia semplice per moduli/componenti internamente complessi – Garantire il disaccoppiamento tra i moduli/componenti poiché minimizza la comunicazione e la dipendenza tra sottosistemi – Offrire un meccanismo di layering • Soluzione – Raggruppare i servizi che un modulo/componente offre all’esterno in un’unica classe che rappresenta la “facciata” del modulo/componente – Definisce un’interfaccia ad un più alto livello di astrazione che rende il sottosistema più semplice da utilizzare E. TINELLI – Ingegneria del Software A. A. 20082008-2009 40 Facade – Struttura e partecipanti • Facade: – Ha conoscenza delle funzionalità di ogni classe del sottosistema. – Delega agli appropriati oggetti del sottosistema ogni richiesta pervenuta dall’esterno. • Subsystem classes: – Implementano le funzionalità del sottosistema – Gestiscono le attività assegnate dal Facade. – Non hanno riferimenti verso il Facade. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 41 Facade - Conseguenze • Favorisce l’incapsulamento - Nasconde ai client i componenti del sottosistema • Promuove l’accoppiamento lasco fra un sottosistema ed i sui client (particolarmente utile quando vengono implementati in modo indipendente) • Non impedisce ai client di utilizzare le classi del sottosistema direttamente qualora sia necessario. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 42 Observer (o Publisher/Subscriber) • • utile quando un elemento (publisher) crea informazioni che sono di interesse per altri elementi (subscriber) L’intento del pattern Observer è definire una dipendenza uno-a-molti tale che quando un oggetto cambia stato tutti quelli che ne dipendono vengono automaticamente notificati del fatto ed aggiornati di conseguenza – L’oggetto osservato è chiamato Subject (soggetto) mentre gli oggetti osservatori sono noti come Observer • ciascun subscriber dovrebbe reagire in un modo proprio quando un publisher genera un evento mentre il publisher dovrebbe mantenere un accoppiamento basso verso i suoi subscriber • Il problema è trovare un modo col quale gli eventi dell’oggetto di riferimento, siano comunicati a tutti gli altri interessati. • il publisher registra dinamicamente i subscriber che sono interessati ai suoi eventi e avvisa quando si verifica un evento secondo 2 modalità: 1. la notifica contiene tutti i dettagli dell’evento 2. notifica che “qualcosa è cambiato” – poi il subscriber, se interessato, interroga il publisher E. TINELLI – Ingegneria del Software A. A. 20082008-2009 43 Observer – Struttura e partecipanti • • • • Subject: – Ha conoscenza dei propri Observer – Fornisce operazioni per l’addizione e cancellazione di oggetti Observer. – Fornisce operazioni per la notifica agli Observer. Observer: Specifica una interfaccia per la notifica di eventi agli oggetti interessati in un Subject ConcreteSubject: – Possiede uno stato dell’interesse dei ConcreteSubject. – Invoca le operazioni di notifica ereditate dal Subject, quando devono essere informati i ConcreteObserver. ConcreteObserver: Implementa l’operazione di aggiornamento dell’Observer. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 44 Applicabilità del pattern Observer - Esempio • Esiste una classe ListaStudenti che mantiene una lista di Studenti iscritti all'esame di Ingegneria del Software, con metodi per l'aggiornamento della stessa • Esiste una classe MatricoleWindow che si occupa della visualizzazione del listato delle matricole mentre un'altra classe DetailsWindow si occupa della visualizzazione di nomi, cognomi, matricole, e contatore finale di iscritti • MatricoleWindow e DetailsWindow sono Observer del Subject ListaStudenti: – ListaStudenti invocherà setChanged() se è stato aggiunto o rimosso uno studente o ne sono stati modificati i dati, e lo notificherà a tutti gli Observer – MatricoleWindow aggiornerà il listato – DetailsWindow farà lo stesso, e in più aggiornerà anche il contatore, se è stato aggiunto o rimosso uno studente E. TINELLI – Ingegneria del Software A. A. 20082008-2009 45 Implementazione in Java • Java fornisce già implementate le classi per realizzare il pattern Observer – Gli osservatori devono implementare l’interfaccia java.util.Observer la quale definisce il metodo • public void update(Observable o,Object arg) – Il subject per essere tale deve estendere la classe java.util.Observable che tra gli altri fornisce i seguenti metodi: Esistono due segnature – public void addObserver(Observer o) una senza argomenti e – public void removeObserver(Observer o) l’altra richiede un Object – public void notifyObserver([Object arg]) come argomento – Protected void setChanged() • Il subject notifica il cambiamento dello stato invocando notifyObserver il quale chiama i metodi update degli osservatori installati quando vengono chiamati in sequenza • Si intende che il subject cambia stato quando viene chiamato il metodo setChanged E. TINELLI – Ingegneria del Software A. A. 20082008-2009 46 Observer – conseguenze • accoppiamento debole (astratto e minimale) tra il publisher e i suoi subscriber – supporto per comunicazione broadcast – possibilità di aggiungere/rimuovere subscriber dinamicamente • potrebbe essere difficile comprendere le relazioni di dipendenza tra i vari elementi • effetto imprevedibile degli aggiornamenti – una modifica in un publisher può scatenare una catena di aggiornamenti e sincronizzazioni su tutti i subscriber • implementazione complessa – se è richiesta una consegna affidabile dei messaggi E. TINELLI – Ingegneria del Software A. A. 20082008-2009 47 Strategy • Consente la definizione di una famiglia d’algoritmi, incapsula ognuno e gli fa intercambiabili fra di loro. Questo permette modificare gli algoritmi in modo indipendente dai clienti che fanno uso di essi. • Lo “Strategy” pattern suggerisce l’incapsulazione della logica di ogni particolare algoritmo, in apposite classi che implementano l’interfaccia che consente agli oggetti “client” di interagire con loro. • Utile quando – Sono necessarie più varianti di un algoritmo (diverse strategie per occupazione di memoria, velocità di esecuzione, ecc.) – L’algortimo usa strutture dati complesse che si vogliono nascondere al client E. TINELLI – Ingegneria del Software A. A. 20082008-2009 48 Strategy – Struttura e partecipanti • Strategy: Dichiara un’interfaccia comune per tutti gli algoritmi supportati. Il Context utilizza questa interfaccia per invocare gli algoritmi definiti in ogni ConcreteStrategy. • ConcreteStrategy: Implementano gli algoritmi che usano l’interfaccia Strategy. • Context: – Viene configurato con un oggetto ConcreteStrategy e mantiene un riferimento verso esso. – Può specificare un’interfaccia che consenta alle Strategy di accedere ai propri dati. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 49 Strategy – Esempio • Il sistema offre delle funzionalità matematiche, mediante l’apposita classe MyArray per la rappresentazione di vettori di numeri mentre la stampa del vettore può avvenire secondo diverse modalità • Il problema è trovare un modo di isolare l’algoritmo che formatta e stampa il contenuto dell’array, per farlo variare in modo indipendente dal resto dell’implementazione della classe. E. TINELLI – Ingegneria del Software A. A. 20082008-2009 50 Strategy – Esempio public class MyArray { private int[] array; private int size; ArrayDisplayFormat format; public MyArray( int size ) { array = new int[ size ]; } public void setValue( int pos, int value ) { array[pos] = value; } public int getValue( int pos ) { return array[pos]; } public int getLength( int pos ) { return array.length; } public void setDisplayFormat( ArrayDisplayFormat adf ) { format = adf; } public void display() { format.printData( array ); } } public class StrategyExample { public static void main (String[] arg) { MyArray m = new MyArray( 10 ); m.setValue( 1 , 6 ); m.setValue( 0 , 8 ); m.setValue( 4 , 1 ); m.setValue( 9 , 7 ); m.setDisplayFormat( new StandardFormat() ); m.display(); m.setDisplayFormat( new MathFormat() ); m.display(); } } E. TINELLI – Ingegneria del Software A. A. 20082008-2009 51 Strategy - Conseguenze • Gestire con maggiore flessibilità una famiglia di algoritmi correlati mediante una gerarchia di classi strategy • Offrire un’alternativa all’ereditarietà – si potrebbe estendere direttamente una classe context per ottenere un comportamento diverso ma questo approccio lega staticamente il comportamento nel context e mescola l’implemenatzione del context con quella dell’algoritmo • I client devono conoscere le diverse strategy disponibili Æ è bene utilizzare il pattern Strategy solo quando le varianti di comportamento sono richieste dal client (eliminando blocchi “case” dal client) E. TINELLI – Ingegneria del Software A. A. 20082008-2009 52 Pattern (Idiomi) and Layers • Livello di Presentazione – I check sugli input e sugli output sono incorporati nelle view (Intercepting Filter) – La gestione delle richieste non è centralizzata (Front controller) • accesso indiscriminato sugli altri livelli • logica di navigazione diffusa su tutte le viste – Le viste presentano delle sezioni comuni quali header, ricerca, autenticazione, carrello, ecc. (Composite View) • Nel livello di dominio – I servizi di dominio non sono gestiti in maniera centralizzata per es. il flusso applicativo è gestito dalle stesse classi che offrono le funzionalità (Facade) • Sorgente Dati – Le classi di dominio del sistema accedono direttamente al database (DAO) E. TINELLI – Ingegneria del Software A. A. 20082008-2009 53 Patterns and Model-View-Controller • MVC disaccoppia le viste dai modelli: – Disaccoppia gli oggetti, in modo che cambiare un oggetto che influenza molti altri oggetti non richiede all’oggetto di conoscere i dettagli degli altri ( pattern Observer ) • MVC permette di annidare le views: – Il pattern Composite descrive il problema più generale di raggruppare oggetti primitivi e compositi in nuovi oggetti con interfacce identiche • MVC controlla la vista mediante il controller ( pattern Strategy ) • MVC usa inoltre i pattern Factory Method e Decorator E. TINELLI – Ingegneria del Software A. A. 20082008-2009 54 Pattern e Specifiche dei Requisiti • R. : “indipendente dal costruttore HW”, “indipendente dal dispositivo”, “deve supportare una famiglia di prodotti” Æ Abstract Factory Pattern • R. : “deve interfacciare un oggetto pre-esistente” Æ Adapter Pattern • R. : “struttura complessa”, “deve avere parametri variabili” Æ Composite Pattern • R. : “deve interfacciare un insieme di oggetti di un sotto-sistema” Æ Façade Pattern • R. : “deve essere estendibile”, “deve essere scalabile” Æ Observer Pattern • R. : “deve fornire politiche indipendenti dai meccanismi di realizzazione” Æ Strategy Pattern E. TINELLI – Ingegneria del Software A. A. 20082008-2009 55