| Design Patters |
|
|
|
![]() In questa lezione, presenteremo il lavoro effettuato dalla “Gang of Four” (GoF) sull’applicazione di modelli (patterns) nella progettazione di software utilizzando un linguaggio a oggetti. Questi modelli, costituiscono, secondo me, una parte fondamentale per i progettisti software, la parte iniziale di un lavoro che a volte può risultare lungo e complesso. D'altronde, spesso ci troviamo a risolvere problemi che si ripetono nel tempo, magari non allo stesso modo, ma in maniera simile a qualcosa che già è stato risolto in precedenza e questo, come capite bene, è un notevole vantaggio, che ci fa risparmiare tempo e fatica. Quindi il riuso del software è il motivo principale per l’uso dei patterns, ma non solo. Supponiamo, ad esempio, di aver progettato un software, di averlo creato senza però aver rispettato un criterio ben preciso e di decidere un giorno di volerlo ampliare. Ma non vorremmo perdere molto tempo a capire il codice che avevamo scritto magari qualche anno fa e poi trovarci molto probabilmente a rifare tutto perché alla fine non c’abbiamo capito proprio nulla. Quindi i benefici principali sono la riduzione del tempo per la ricerca delle soluzioni, e la riduzione del tempo per la progettazione delle applicazioni. Beh, non poco direi. Andiamo allora a trattare, in linee generali per ragioni di spazio, i principali patterns sui 23 proposti dal Gof. Se dopo questa lezione resterete affascinati da questi potenti strumenti, potrete approfondire l’argomento consultando il libro “Design Patterns: Elements of Reusable Object-Oriented Software” disponibile anche in versione italiana. Ps. Gli esempi che esporremo vengono presentati in linguaggio Java, ma valgono per tutti i linguaggi a oggetti (es. C++, C#, SmallTalk, ecc.). Vi proporrò 6 patterns di uso più comune e cercherò di convincervi che il loro uso vi semplificherà di molto la vita vostra, degli altri e di quella del vostro software. IteratorFornisce un modo di accedere sequenzialmente agli oggetti presenti in una collezione, senza esporre la rappresentazione interna di questa. EsempioIl percorso di un viaggiatore è rappresentato come una collezione ordinata di oggetti, dove ogni oggetto rappresenta un luogo visitato. La collezione può essere implementata in base ad un’array, una linked list o qualunque altra struttura. Una applicazione sarebbe interessata ad accedere agli elementi di questa collezione in una sequenza particolare, ma senza dover interagire direttamente con il tipo di struttura interna. Descrizione offerta dal patternIl pattern “Iterator” suggerisce l’implementazione di un oggetto che consenta l’acceso e percorso della collezione, e che fornisca una interfaccia standard verso chi è interessato a percorrerla e ad accede agli elementi. StrategyConsente 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. EsempioLa progettazione di una applicazione che offre delle funzionalità matematiche, considera la gestione di una apposita classe (MyArray) per la rappresentazione di vettori di numeri. Tra i metodi di questa classe si ha definito uno che esegue la propria stampa. Questo metodo potrebbe stampare il vettore nel seguente modo (chiamato, ad es. MathFormat): { 12, -7, 3, … } oppure di questo altro modo (chiamato, ad. es. SandardFormat): Arr[0]=12 Arr[1]=-7 Arr[2]=3 … E’ anche valido pensare che questi formati potrebbero posteriormente essere sostituiti da altri. 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. Descrizione offerta dal patternLo “Strategy” pattern suggerisce l’incapsulazione della logica di ogni particolare algoritmo, in apposite classi (ConcreteStrategy) che implementano l’interfaccia che consente agli oggetti MyArray (Context) di interagire con loro. Questa interfaccia deve fornire un accesso efficiente ai dati del Context, richiesti da ogni ConcreteStrategy, e viceversa. VisitorRappresenta una operazione da essere eseguita in una collezione di elementi di una struttura. L’operazione può essere modificata senza alterare le classi degli elementi dove opera. EsempioSi consideri una struttura che contiene un insieme eterogeneo di oggetti, su i quali bisogna applicare la stessa operazione, che però è implementata in modo diverso da ogni classe di oggetto. Questa operazione potrebbe semplicemente stampare qualche dato dell’oggetto, formattato in un modo particolare. Per esempio la collezione potrebbe essere un Vector che ha dentro di se oggetti String, Integer, Double o altri Vector. Si noti che se l’oggetto da stampare è un Vector, questo dovrà essere scandito per stampare gli oggetti trovati ai suoi interni. Si consideri anche che l’operazione ad applicare non è in principio implementata negli oggetti appartenenti alla collezione, e che questa operazione potrebbe essere ulteriormente ridefinita. Un approccio possibile sarebbe creare un oggetto con un metodo adeguato per scandire collezioni o stampare i dati dell’oggetto. Questo approccio va bene se si vuole lavorare con pochi tipi di dati, ma intanto questi aumentano il codice diventa una lunga collezione di if…else. Il problema è trovare un modo di applicare questa operazione a tutti gli oggetti, senza includerla nel codice delle classi degli oggetti. Descrizione offerta dal patternLa soluzione consiste nella creazione di un oggetto (ConcreteVisitor), che è in grado di percorrere la collezione, e di applicare un metodo proprio su ogni oggetto (Element) visitato nella collezione (avendo un riferimento a questi ultimi come parametro). Per agire in questo modo bisogna fare in modo che ogni oggetto della collezione aderisca ad un’interfaccia (Visitable), che consente al ConcreteVisitor di essere “accettato” da parte di ogni Element. Poi il Visitor, analizzando il tipo di oggetto ricevuto, fa l’invocazione alla particolare operazione che in ogni caso si deve eseguire. DecoratorAggiunge dinamicamente responsabilità addizionali ad un oggetto. In questo modo si possono estendere le funzionalità d’oggetti particolari senza coinvolgere complete classi. EsempioSi pensi ad un modello di oggetti che rappresenta gli impiegati (Employee) di una azienda. Tra gli impiegati, ad esempio, esistono gli Ingegneri (Engineer) che implementano le operazioni definite per gli impiegati, secondo le proprie caratteristiche. Il sistema comprende la possibilità di investire gli impiegati con delle responsabilità aggiuntive, ad esempio, quando un impiegato diventa capoufficio (Administrative Manager), oppure, quando viene assegnato alla direzione di un progetto (Project Manager), essendo entrambe responsabilità non esculenti tra di loro. Questi cambiamenti di tipologia di alcuni impiegati coinvolgono modifiche delle responsabilità definite per gli oggetti, alterandone le esistenti o aggiungendone nuove. Per questa ragione, sarebbe di interesse definire un modo per aggiungere dinamicamente nuove responsabilità ad oggetto specifico, eventualmente con la ulteriore possibilità di toglierle. Descrizione offerta dal patternIl pattern suggerisce la creazione di wrapper classes (Decorator) che racchiudono gli oggetti ai quali si vuole aggiungere le nuove responsabilità. Questi ultimi oggetti, insieme ai Decorator devono implementare una interfaccia comune, in modo che l’applicazione possa continuare ad interagire con gli oggetti decorati. Per una stessa interfaccia possono esserci più Decorator, ad esempio, per investire i ruoli di capoufficio e di responsabile di un progetto. Il fatto che Decorator e oggetti decorati implementino la stessa interfaccia, consente anche l’applicazione di un Decorator ad un altro oggetto già decorato, ottenendo in questo modo la sovrapposizione di funzioni (ad esempio, un impiegato potrebbe essere investito come capoufficio e responsabile di un progetto contemporaneamente). ObserverConsente la definizione di associazioni di dipendenza di molti oggetti verso di uno, in modo che se quest’ultimo cambia il suo stato, tutti gli altri sono notificati e aggiornati automaticamente. EsempioAd un oggetto (Subject) vengono comunicati diversi numeri. Questo oggetto decide in modo casuale di cambiare il suo stato interno, memorizzando il numero ad esso proposto. Altri due oggetti incaricati del monitoraggio dell’oggetto descritto (un Watcher e un Psychologist), devono avere notizie di ogni suo singolo cambio di stato, per eseguire i propri processi di analisi. Il problema è trovare un modo nel quale gli eventi dell’oggetto di riferimento, siano comunicati a tutti gli altri interessati. Descrizione offerta dal patternIl pattern “Observer” assegna all’’oggetto monitorato (Subject) il ruolo di registrare ai suoi interni un riferimento agli altri oggetti che devono essere avvisati (ConcreteObservers) degli eventi del Subject, e notificarli tramite l’invocazione a un loro metodo, presente nella interfaccia che devono implementare (Observer). Questo pattern è stato proposto originalmente dai GoF per la manutenzione replicata dello stato del ConcreteSubject nei ConcreteObserver, motivo per il quale sono previsti una copia dello stato del primo nei secondo, e la esistenza di un riferimento del ConcreteSubject nel ConcreteObserver. Nonostante lo espresso nel parafo precedente, si deve tenere in conto che questo modello può servire anche a comunicare eventi, in situazioni nelle quali non sia di interesse gestire una copia dello stato del Subject. Da un'altra parte si noti che non è necessario che ogni ConcreteObserver abbia un riferimento al Subject di interesse, oppure, che i riferimenti siano verso un unico Subject. Le Java API offrono un modello esteso in funzionalità, allineato in questa direzione, che sarà l’approccio utilizzato in questo esempio. Un’altra versione del pattern Observer, esteso con una gestione più completa degli eventi, è implementato dentro l’ambiente di sviluppo G++ [16]. In questo modello è consentita la registrazione del Observer presso il Subject, indicando addizionalmente il tipo di evento davanti al quale l’Observer deve essere notificato, e la funzione del Observer da invocare. StateConsente ad un oggetto modificare il suo comportamento quando il suo stato interno cambia. EsempioSi pensi ad un orologio che possiede due pulsanti: MODE e CHANGE. Il primo pulsante serve per settare il modo di operazione di tre modi possibili: “visualizzazione normale”, “modifica delle ore” o “modifica dei minuti”. Il secondo pulsante, invece, serve per accendere la luce del display, se è in modalità di visualizzazione normale, oppure per incrementare in una unità le ore o i minuti, se è in modalità di modifica di ore o di minuti. In questo esempio, un approccio semplicistico conduce all’implementazione del codice di ogni operazione come una serie di decisioni: operation buttonCHANGEpressed{ if( clockState = NORMAL_DISPLAY ) displayTimeWithLight(); else if( clockState = UPDATING_HOURS
) hours++; else if( clockState =
UPDATING_MINUTES ) minutes++; ... } Il problema di questo tipo di codice è che si rende più difficile la manutenzione, perché la creazione di nuovi stati comporta la modifica di tutte le operazioni dove essi sono testati. Da un'altra parte non si tiene una visione dello stato, in modo di capire come agisce l’oggetto (l’orologio in questo caso), a seconda del proprio stato, perché questo comportamento è spezzato dentro l’insieme di operazioni disponibili. Si vuole definire un meccanismo efficiente per gestire i diversi comportamenti che devono avere le operazioni di un oggetto, secondo gli stati in cui si trovi. Descrizione offerta dal patternIl pattern “State” suggerisce incapsulare, all’interno di una classe, il modo particolare in cui le operazioni di un oggetto (Context) vengono svolte quando lo si trova in quello stato. Ogni classe (ConcreteState) rappresenta un singolo stato possibile del Context e implementa una interfaccia comune (State) contenente le operazioni che il Context delega allo stato. L’oggetto Context deve tenere un riferimento al ConcreteState che rappresenta lo stato corrente. Puoi Commentare l'articolo sul Forum Opinioni
Realizzato Da Gianfrix In Esclusiva Per TiempoLibreSite.com
|
Nessun commento postato
mXcomment 1.0.8 © 2007-2008 - visualclinic.fr
License Creative Commons - Some rights reserved
| < Prec. | Pros. > |
|---|


















