Anda di halaman 1dari 28

Sviluppo Applicazioni Software

Pattern GRASP
&
Design Pattern

Enrico Mensa,
Basato sulle lezioni della prof. Viviana Bono
Sviluppo Applicazioni Software
Indice degli argomenti

Pattern GRASP
1) I nove pattern 1
1.1) Information Expert 1

1.2) Creator 1

1.3) Controller 1

1.4) Low Coupling 1

1.5) High Cohesion 1

1.6) Poymorphism 1

1.7) Pure Fabrication 1

1.8) Indirection 2

1.9) Protected Variations 2

Design Pattern
1) I design pattern 3
2) Creational patterns (GoF) 3
2.1) Abstract Factory 3

2.2) Factory Method 5

2.3) Singleton 7

3) Structural patterns (GoF) 8


3.1) Adapter 8

3.2) Composite 10

3.3) Decorator 12

3.4) Façade 14

3.5) Proxy 16

4) Behavioral patterns (GoF) 18


4.1) State 18

4.2) Strategy 20

4.3) Visitor 22

5) Pattern non GoF 26


5.1) Static Factory 26

Chiaramente i metodi create saranno metodi statici. 26

5.2) Concrete Factory 26


Sviluppo Applicazioni Software
Pattern GRASP & Design Pattern

Pattern GRASP
1) I nove pattern
GRASP sta per General Responsability Assignment Software Patterns e sono proprio questo: linee guida per
l’assegnazione di responsabilità a classi e oggetti.

GRASP è una sorta di strumento “mentale”, un sostegno didattico per la progettazione di software.
Distinguiamo nove pattern principali ognuno rappresentante le tipiche problematiche che si riscontrano in
ambiente OO.

1.1) Information Expert


Fornisce i modelli generali associati all’assegnazione delle responsabilità agli oggetti e stabilisce che la
responsabilità deve essere assegnata ad una classe designata come Information Expert (possiede tutte le
informazioni essenziali).
L’incapsulamento e la decentralizzazione delle responsabilità hanno un guadagno. Maggiore leggibilità, maggiore
riuso del codice.

1.2) Creator
Il pattern Creator si occupa del problema della “new”. Designa in sostanza le classi che devono creare istanze di altre
classi. Aumenta l’incapsulamento e la leggibilità, il senso di connessione fra le classi è più chiaro (se B è sempre
creato da A, allora B è in un certo senso “dipendente” da A, e per creare B sappiamo che dobbiamo passare sempre
per A!).

1.3) Controller
Con il Controller andiamo a trattare l’annoso problema delle chiamate ad un classe che rappresenta l’intero sistema
in uno scenario di caso d’uso. Il pattern definisce la classe come il primo oggetto che riceve e coordina operazioni di
sistema relativamente ad una certa porzione del sistema stesso (ad esempio UtenteController gestirà CreaUtente ed
EliminaUtente).

1.4) Low Coupling


Il Low Coupling (basso accoppiamento) è un pattern che stabilisce come assegnare le responsabilità per quanto
riguarda la dipendenza fra classi (la si vuole al minimo indispensabile), il basso impatto in una classe dal
cambiamento di un’altra classe (anche questo vuole essere minimizzato) e il riuso (vuole essere massimizzato).

1.5) High Cohesion


Si vogliono mantenere, tramite l’High Choesion, oggetti focalizzati, gestibili e intelligibili in maniera appropriata.
Spesso un basso accoppiamento porta ad un’alta coesione.

1.6) Poymorphism
La responsabilità di definizione delle variazioni dei comportamenti basati su tipo viene assegnata ai tipi per i quali
avviene tale variazione.

1.7) Pure Fabrication


È una classe creata appositamente per permettere High Cohesion e Low Coupling, in realtà, però, non esiste nel
dominio del problema

EM
Sviluppo Applicazioni Software - Capitolo 1
Pattern GRASP & Design Pattern

1.8) Indirection
Il pattern Indirection supporta il basso accoppiamento tra due elementi con l’ausilio di un oggetto intermediario
fra due. Classico esempio è l’MVC (il controller).

1.9) Protected Variations


Il pattern Protected Variatons protegge gli elementi dalle variazioni compiute in altri elementi. L’uso di interfacce e
di polimorfismo aiuta in questo senso.

EM
Sviluppo Applicazioni Software
Pattern GRASP & Design Pattern

Design Pattern
1) I design pattern
Un design pattern è “una soluzione progettuale generale ad un problema ricorrente”.
Studiamo alcuni design pattern attingendo dal libro “Design Patterns: Elements of Reusable Object-Oriented Software “
della Gang Of Four.

2) Creational patterns (GoF)


2.1) Abstract Factory

Intent
Fornire un’interfaccia per creare famiglie di oggetti correlati o dipendenti fra loro senza specificare le loro classi
concrete in fase di creazione.

AKA
Kit.

Motivation
Un’applicazione dispone di diverse skin: gli sviluppatori dei widget dell’applicazione non possono fissare il codice
della skin all’interno del loro codice per far sì che queste siano facilmente intercambiabili e che i widget funzionino
per ogni skin. L’abstract factory risolve questo problema.

Structure

- AbstractFactory (WidgetFactory): declares an interface for operations that create abstract product objects.
- ConcreteFactory (MotifWidgetFactory, PMWidgetFactory): implements the operations to create concrete product
objects.
- AbstractProduct (Window, ScrollBar): declares an interface for a type of product object.
- ConcreteProduct (MotifWindow, MotifScrollBar) : defines a product object to be created by the corresponding
concrete factory, implements the AbstractProduct interface.
- Client: uses only interfaces declared by AbstractFactory and AbstractProduct classes.

EM
Sviluppo Applicazioni Software - Capitolo 1
Pattern GRASP & Design Pattern

Example of use
Abbiamo un toolkit con interfaccia che supporta multiple skin, ad esempio Motif e Presentation Manager (PM).
Differenti skin definiscono differenti caratteristiche visive per i widget che l’applicazione supporta (quindi differenti
scrollbars, finestre, bottoni, ecc.).
Per essere portabili su diverse skin, l’applicazione non dovrebbe fissare il codice dei widget ad una particolare skin.

Per risolvere il problema definiamo una classe astratta WidgetFactory che dichiara un’interfaccia per creare ogni tipo
base di widget. Ogni widget ha quindi una classe astratta implementata da diverse subclass per ogni specifica skin.
La WidgetFactory ha un’operazione che ritorna il widget per ogni interfaccia di widget esistente (quindi che fa una
new). I client chiameranno questa operazione per ottenere istanze di widget ma non sapranno quale sarà la classe
concreta che stanno usando, quindi, i client sono esterni rispetto alla skin.

C’è una sottoclasse concreta di WidgetFactory per ogni skin.


Ogni sottoclasse implementa le operazioni per creare l’appropriato widget per una certa skin.
Ad esempio il metodo CreateScrollBar nel MotifWidgetFactory istanzierà e ritornerà un oggetto MotifScrollBar.
Il client potrà creare widget solamente sfruttando la WidgetFactory.

Consequences
✓ Le classi concrete sono isolate (maggiormente controllabili).
✓ È semplice cambiare famiglia di prodotti: passare da una skin all’altra è semplice poiché basta cambiare
riferimento chiedendone uno nuovo alla Factory.
✓ La consistenza del prodotto è più forte, poiché una sola skin viene usata alla volta.
- Estendere con nuove tipologie di prodotto è difficile. Aggiungere una abstract factory risulta essere costoso,
questo poiché la WidgetFactory con la sua interfaccia designa quali siano le operazioni utilizzabili e quindi
l’aggiunta di una nuova tipologia di prodotto implicherebbe una riscrittura sia della WidgetFactory che di tutte le
sue sottoclassi.

EM
Sviluppo Applicazioni Software
Pattern GRASP & Design Pattern

2.2) Factory Method

Intent
Definire un’interfaccia per creare un oggetto ma lasciare alle sottoclassi la scelta quale classe intanziare. Il factory
method permette ad una classe di deferire l’istantanziazione alle sottoclassi.

AKA
Virtual Constructor

Motivation
Se abbiamo un framework vi saranno situazioni in cui vogliamo istanziare una classe ma conosciamo solamente la
classe astratta (ad esempio quando un client definisce delle sue sottoclassi a classi appartenenti al framework) che
non può istanziare. Il factory method offre una soluzione a questo problema.

Structure

- Product (Document): defines the interface of objects the factory method creates.
- ConcreteProduct (MyDocument): implements the Product interface.
- Creator (Application):
- Declares the factory method, which returns an object of type Product. Creator may also define a default
implementation of the factory method that returns a default ConcreteProduct object.
- May call the factory method to create a Product object.
- ConcreteCreator (MyApplication): overrides the factory method to return an instance of a ConcreteProduct.

EM
Sviluppo Applicazioni Software - Capitolo 1
Pattern GRASP & Design Pattern

Example of use
I framework usano le classi astratte per definire e mantenere le relazioni fra gli oggetti. Un framework è anche,
spesso, il responsabile dell’istanziazione di quegli oggetti.

Consideriamo un framework per applicazioni che può presentare diversi documenti all’user.
Due astrazioni fondamentali di questo framework saranno le classi Application e Document.
Entrambe sono classe astratte e il cliente dovrà subclassarle per realizzare la sua specifica implementazione
dell’applicazione.
Per creare ad esempio un’applicazione di disegno, definiremo le classi DrawingApplication e DrawingDocument,
rispettivamente sottoclassi di Application e Document.
La classe Application è responsabile per il managament dei Documents e li creerà quando l’utente lo richiede
(possiamo immaginare un tasto New nell’interfaccia).

Giacché la classe Document è subclassata nell’applicazione specifica l’Application class non potrà sapere quale sia la
subclass di Document da applicare (nel nostro caso vorremmo DrawingDocument): il framework vuole istanziare
una classe ma conosce solo la classe astratta.
Il factory method ci corre in aiuto incapsulando la conoscenza relativa a quale sia la sottoclasse corretta di
Document e la esporta al di fuori del framework.

Le sottoclassi di Application ridefiniscono il metodo CreateDocument così da ritornare un oggetto di tipo corretto
(la nostra Document subclass). Il CreateDocument è il nostro factory method.

Consequences
✓ Vengono forniti hooks per le sottoclassi (più flessibilità)

Alcune note per l’implementazione in in Java


In Java notiamo che:
- Tutto questo funziona perché abbiamo i costruttori non ereditati.
- I metodi statici non hanno binding dinamico (viene eseguito il metodo statico del tipo e non del reale oggetto).

EM
Sviluppo Applicazioni Software
Pattern GRASP & Design Pattern

2.3) Singleton

Intent
Assicurarsi che una classe abbia una sola istanza, fornendo un accesso globale all’istanza stessa.

Motivation
Talvolta è importante che alcune classi abbiano esattamente una sola istanza. Ad esempio, un solo file system, un
solo window manager, ecc.
Come fare? Una variabile globale è sempre accessibile, ma non ci garantisce che non vengano istanziati oggetti
multipli. È meglio creare una classe che sia responsabile di sé stessa da questo punto di vista: tale classe garantirà sia
l’accesso all’istanza se presente e garantirà che quell’istanza sia l’unica in tutto il sistema.

Structure

Singleton:
- Defines an Instance operation that lets clients access it sunique instance. Instance is a class member.
- May be responsible for creating its own unique instance.

Chiaramente avremo il costruttore della classe privato.

Consequences
✓ Accesso controllato ad una istanza.
✓ Name space ridotto: il singleton riduce la quantità di variabili globali.
✓ Rappresentazione più raffinata: subclassando una classe singleton possiamo a sua volta avere estensioni singleton
controllate.
✓ Permette un numero di istanze con cardinalità precisa: se invece di una istanza sola volessimo limitare le istanze a
un numero ben definito potremmo facilmente farlo implemendando un controllo differente nel metodo che
costruisce il singleton.

EM
Sviluppo Applicazioni Software - Capitolo 1
Pattern GRASP & Design Pattern

3) Structural patterns (GoF)

3.1) Adapter
Intent
Convertire l’interfaccia di una classe in un’altra interfaccia. L’adapter permette ad alcune classi incompatibili per via
delle interfacce di lavorare insieme come se l’incompatibilità non esistesse.

AKA
Wrapper.

Motivation
Talvolta delle librerie che sono state disegnate per il riuso non possono essere riusate solamente perché sono
cambiate le interfacce verso l’esterno (ma i metodi sono ancora tutti gli stessi). L’adapter risolve questo problema.

Structure
Una classe adapter usa l’ereditarietà multipla per adattare una interfaccia in un’altra (chiamato class adapter):

Nel caso in cui non sia possibile usare l’ereditarietà multipla, si adotta questa tecnica (chiamato object adapter):

- Target (Shape): defines the domain-specific interface that Client uses.


- Client (DrawingEditor): collaborates with objects conforming to the Target interface.
- Adaptee (TextView): defines an existing interface that needs adapting.
- Adapter (TextShape): adapts the interface of Adaptee to the Target interface.

EM
Sviluppo Applicazioni Software
Pattern GRASP & Design Pattern

Example of use
Consideriamo per esempio un drawing editor che permette all’utente di disegnare elementi grafici (linee, poligoni,
testo) dentro a immagini e diagrammi. L’oggetto chiave di astrazione di questo editor è l’oggetto grafico che ha uno
shape editabile (avrà il contenuto dell’oggetto) e un metodo per “disegnarsi”. Tale oggetto possiamo chiamarlo
Shape. Shape sarà in realtà una interfaccia, quindi avremo classi quali LineShape, PolygonShape, ecc.
Mentre le classi per linee e poligoni sono relativamente semplici da implementare, classi come quella di testo (la
TextShape) possono essere ben più complesse da implementare (basti pensare a screenupdate e buffer
management per il testo).
Supponiamo intanto che un software off-the-shelf abbia creato una TextView sofisticata e ben fatta: sarebbe bello
per noi poter sfruttare la TextView implementata in questo software come TextShape dell’editor! Questo è però
impossibile poiché la TextView è stata sviluppata senza considerare l’interfaccia Shape dell’editor (ovviamente, è un
lavoro a parte).
La prima idea potrebbe essere quella di modificare l’interfaccia della TextView ma ciò è possibile solamente se
avessimo il codice sorgente (non è sempre così, anzi!): si tratterebbe comunque di un lavoro lungo e poco
conveniente (tutto lavoro per una applicazione sola).
Invece potremmo definire la classe TextShape in modo che si adatti all’interfaccia Shape! Vi sono due modi di fare
questo:
1) Sfruttare l’ereditarietà multipla ed ereditare l’interfaccia Shape e l’implementazione TextView,
2) Comporre una istanza di TextView all’interno di un TextShape in termini dell’interfaccia TextView (si sfruttano gli
oggetti).

Vediamo come il metodo BoundingBox() della classe TextShape sia rimappato sul metodo GetExtend() della classe
TextView.

Consequences
Per la versione class, un class adapter:
- Adapts Adaptee to Target by committing to a concrete Adapter class. As a consequence, a class adapter won't work
when we want to adapt a class and all its subclasses.
- Lets Adapter override some of Adaptee's behavior, since Adapter is a subclass of Adaptee.
- Introduces only one object, and no additional pointer indirection is needed to get to the adaptee.

Per la versione object, un object adapter:


- Lets a single Adapter work with many Adaptees—that is, the Adaptee itself and all of its subclasses (if any). The
Adapter can also add functionality to all Adaptees at once.
- Makes it harder to override Adaptee behavior. It will require subclassing Adaptee and making Adapter refer to the
subclass rather than the Adaptee itself.

EM
Sviluppo Applicazioni Software - Capitolo 1
Pattern GRASP & Design Pattern

3.2) Composite

Intent
Comporre oggetti in strutture ad albero per rappresentare gerarchie. Oggetti composti e oggetti singoli sono
trattati allo stesso modo dall’utente.

Motivation
Applicazioni di disegno di grafi e di disegno di schemi permettono all’utente di unire diversi elementi del
diagramma per comporre elementi più complessi. Potremmo pensare di definire delle classi per gli elementi atomici
(ad esempio Text o Line) e poi delle altre classi che fungano da container delle primitive.
Questo approccio ha però un problema: il codice che tratta queste classi deve trattare in modo differenziata gli
atomi e gli oggetti composti. Questo aumenta la complessità dell’applicazione.

Structure

Come vediamo, ogni oggetto Composite può contenere più Component fino a che il Component sarà
inevitabilmente una Leaf.
Gli oggetti Component sono organizzati in una lista di
Component, chiamata children.

- Component (Graphic): declares the interface for


objects in the composition, implements default
behavior for the interface common to all classes as
appropriate, declares an interface for accessing and
managing its child components, (optional) defines an
interface for accessing a component's parent in the
recursive structure, and implements it if that's appropriate.
- Leaf (Rectangle, Line, Text, etc.): represents leaf objects in the composition, a leaf has no children, defines behavior
for primitive objects in the composition.
- Composite (Picture): defines behavior for components having children, stores child components, implements
child-related operations in the Component interface.
- Client: manipulates objects in the composition through the Component interface.

10

EM
Sviluppo Applicazioni Software
Pattern GRASP & Design Pattern

Example of use
Abbiamo il nostro software di disegno di grafi, come detto prima. Potremmo immaginare una siffatta struttura:

Siamo così in grado di rappresentare facilmente oggetti di questo genere:

Consequences
✓ Oggetti semplici e oggetti complessi sono trattati tramite la stessa interfaccia.
✓ È possibile aggiungere incrementalmente nuovi componenti senza intaccare la struttura precedente.
- È difficile restringere le componenti incluse in un oggetto composite. Il type system non è più utilizzabile per
questo scopo, occorrerà usare metodi di controllo run-time.

11

EM
Sviluppo Applicazioni Software - Capitolo 1
Pattern GRASP & Design Pattern

3.3) Decorator

Intent
Assegnare responsabilità aggiuntive ad un oggetto in modo dinamico. Il decorator è un’alternativa flessibile al
subclassing per funzionalità più estese.

AKA
Wrapper.

Motivation
Talvolta vogliamo aggiungere responsabilità a singoli oggetti e non ad una classe intera. Un modo per aggiungere
responsabilità è l’ereditarietà, ma questo approccio è statico (bisogna decidere a monte a chi assegnare cosa). Noi
vogliamo scegliere quando aggiungere o rimuovere una certa caratteristica ad un oggetto.

Structure

- Component (VisualComponent): defines the interface for objects that can have responsibilities added to them
dynamically.
- ConcreteComponent (TextView) defines an object to which additional responsibilities can be attached.
- Decorator: maintains a referenceto a Component object and defines an interface that conforms to Component's
interface.
- ConcreteDecorator (BorderDecorator, ScrollDecorator): adds responsibilities to the component.

12

EM
Sviluppo Applicazioni Software
Pattern GRASP & Design Pattern

Example of use

Supponiamo di avere una TextView che mostra testo all’interno di una finestra. Di default, la finestra non ha né scroll
né bordo.
Potremmo desiderare di avere in certi momenti una ScrollBar e in altri no: quando ci serve questa caratteristica,
semplicemente decoriamo l’oggetto base TextWindow con un oggetto ScrollBar.

Come vediamo, l’oggetto TextWiew viene puntato dai vari decorator. Avremo bisogno di un metodo comune a tutte
le classi che ci permetta di “percorrere” il decorator.
Perciò, la struttura applicata all’esempio risulterà essere:

Consequences
✓ Più flessibilità rispetto all’ereditarietà statica.
✓ È possibile aggiungere incrementalmente nuove decorazioni al sistema senza intaccare il sistema precedente e
senza aumentare la complessità.
- Un decorator e le sue componenti non sono identiche: il decorator fornisce trasparenza ma da un punto di vista di
identità dell’oggetto, un componente decorato non è identico al componente stesso.
- Ci sono molti piccoli oggetti che si assomigliano.

13

EM
Sviluppo Applicazioni Software - Capitolo 1
Pattern GRASP & Design Pattern

3.4) Façade
Intent
Nell’ambito di un sottosistema si vuole fornire una unica interfaccia ad un set di interfacce. Façade (che significa
facciata) definisce quindi una interfaccia più ad alto livello rendendo così il sottosistema più semplice da usare.

Motivation
Strutturare un sistema in sottosistemi riduce la complessità. Una tecnica classica è quella di ridurre le comunicazioni
e le dipendenze fra i sottosistemi, ecco quindi che il façade fa al caso nostro poiché raggruppa più interfacce in un
solo punto del codice.

Structure

- Facade (Compiler): knows which subsystem classes are responsible for a request. o delegates client requests to
appropriate subsystem objects.
- Subsystem classes (Scanner, Parser, ProgramNode, etc.): implement subsystem functionality, handle work assigned
by the Facade object, have no knowledge of the facade; that is, they keep no references to it.

14

EM
Sviluppo Applicazioni Software
Pattern GRASP & Design Pattern

Example of use
Pensiamo ad un ambiente di programmazione che fornisce accesso alle applicazioni esterne al suo sistema di
compilazione.
Questo sottosistema avrà classi quali Parser, Scanner, ProgramNote, Bytecodestream, ProgramNodeBuilder, ecc.
Queste componenti sono essenziali e implementano a tutti gli effetti il compilatore.
La maggior parte dei client, d’altronde, non hanno interesse nell’andare a lavorare su queste classi così dettagliate
ma tendenzialmente vorranno solo poter compilare del codice. L’uso delle interfacce più low-level rappresenta per
questi utenti un problema.

Ecco quindi che si definisce una interfaccia più ad alto livello che fa da “scudo” e che maschera le classi low-level
dietro a chiamate che raggruppano le funzionalità del compilatore.

Consequences
✓ Il client è “schermato” dalle componenti del sottosistema (uso più semplice del sistema).
✓ Promuove basso accoppiamento fra il sottosistema ed il suo client.
✓ Non preclude l’utente dall’uso delle interfacce più low-level se lo desidera.

15

EM
Sviluppo Applicazioni Software - Capitolo 1
Pattern GRASP & Design Pattern

3.5) Proxy

Intent
Fornire un placeholder per un’altro oggetto per controllarne l’accesso.

AKA
Surrogate

Motivation
Una delle ragioni per cui controllare l’accesso ad un oggetto è posticipare il più possibile la sua creazione ed
inizializzazione, questo perché è un’operazione tipicamente costosa e quindi è saggio farla solamente nel momento
in cui si serve davvero l’oggetto. Vi sono quattro applicazioni (remote, virtual, protection e smart).

Structure

Quindi una possibile esecuzione avrà gli oggetti collegati in questo modo:

- Proxy (ImageProxy):
- Maintains a reference that lets the proxy access the real subject. Proxy may refer to a Subject if the
RealSubject and Subject interfaces are the same.
- Provides an interface identical to Subject's so that a proxy can by substituted for the real subject.
- Controls access to the real subject and may be responsible for creating and deleting it.
- Other responsibilities depend on the kind of proxy:
- Remote proxies are responsible for encoding a request and its arguments and for sending the
encoded request to the real subject in a different address space.
- Virtual proxies may cache additional information about the real subject so that they can postpone
accessing it. For example, the ImageProxy from the Motivation caches the real image's extent.
- Protection proxies check that the caller has the access permissions required to perform a request.
- Subject (Graphic): defines the common interface for RealSubject and Proxy so that a Proxy can be used anywhere a
RealSubject is expected.
- RealSubject (Image): defines the real object that the proxy represents.

16

EM
Sviluppo Applicazioni Software
Pattern GRASP & Design Pattern

Example of use (virtual proxy)


Ad esempio aprire un documento è di per sé non molto costoso, ma invece caricarne il contenuto lo è. Giacché non
tutto il contenuto verrà visualizzato nello stesso istante, dovremo effettuare delle istanziazioni on demand.
Pensiamo al caricamento di una immagine: quando il documento sarà sulla pagina che contiene l’immagine allora la
caricheremo: come possiamo mascherare tutto questo all’implementazione? Non vorremmo che questa
ottimizzazione impattasse, ad esempio, sul rendering.
Si usa una classe proxy che viene adoperata come “segnaposto” per la reale immagine.

Il proxy istanzierà l’immagine solamente nell’istante in cui ci servirà veramente. Chiaramente il proxy terrà
riferimento all’immagine reale una volta istanziata.

Applicability
È possibile usare il proxy in situazioni differenti:
- Remote proxy: fornisce una rappresentazione locale di un oggetto che si trova in un’altra zona di memoria
indirizzabile (lo stub di Java).
- Virtual proxy: crea oggetti costosi on demand.
- Protection proxy: controlla l’accesso all’oggetto originale (pensiamo ad un oggetto che a seconda delle
credenziali viene visualizzato in modo diverso oppure può essere visualizzato solo da utenti autenticati).
- Smart reference: talvolta è necessario effettuare alcune operazioni prima dell’accesso ad un oggetto, ad esempio:
- Contare il numero di riferimenti all’oggetto (free di memoria).
- Copiare una copia persistente dell’oggetto.
- Controllare che l’oggetto non sia lockato.

Consequences
✓ Un remote proxy crea astrazione dal luogo in cui un oggetto si trova effettivamente (stub dei socket Java).
✓ Un virtual proxy fornisce un’ottimizzazione (si caricano gli oggetti solo nel momento in cui servono).
✓ Codice più pulito poiché le operazioni che riguardano un oggetto nel momento della sua istanziazione sono tutte
radunate.
17

EM
Sviluppo Applicazioni Software - Capitolo 1
Pattern GRASP & Design Pattern

4) Behavioral patterns (GoF)

4.1) State

Intent
Permette ad un oggetto di modificare il suo comportamento quando il suo stato interno cambia.

AKA
Object for States

Motivation
Pensiamo ad un oggetto i cui metodi devono agire in modo differente a seconda dello stato in cui l’oggetto si trova.
Onde evitare molti if-else e istruzioni condizionali, il pattern state fa sì che sia l’intero oggetto a cambiare stato e
quindi ad eseguire il metodo corretto a seconda della situazione in cui si trova.

Structure

- Context (TCPConnection): defines the interface of interest to clients, maintains an instance of a ConcreteState
subclass that defines the current state.
- State (TCPState): defines an interface for encapsulating the behavior associated with a particular state of the
Context.
- ConcreteState subclasses (TCPEstablished, TCPListen, TCPClosed): each subclass implements a behavior associated
with a state of the Context.

18

EM
Sviluppo Applicazioni Software
Pattern GRASP & Design Pattern

Example of use
Consderiamo una classe TCPConnection che rappresenta una connessione network. La connessione può avere
diversi stati, possiamo immaginare Established, Listing, Closed. Quando viene chiamato il metodo open() su un
oggetto TCPConnection, possiamo immaginare che a seconda dello stato in cui esso si trova avremo
comportamenti differenti.

La classe TCPConnection mantiene un oggetto Stato che è un’interfaccia dietro alla quale si trovano i vari stati che
reimplementano i metodi in modo coerente a seconda della classe (ovvero dello stato TCP).

Consequences
✓ Permette una migliore visualizzazione degli stati di un oggetto (sono tutti sotto la stessa interfaccia).
✓ Esplicita il cambio di stato.
✓ Gli stati degli oggetti possono essere condivisi da upper-oggetti diversi.

19

EM
Sviluppo Applicazioni Software - Capitolo 1
Pattern GRASP & Design Pattern

4.2) Strategy

Intent
Definisce una famiglia di algoritmi, ognuno dei quali è incapsulato, e li rende intercambiabili.

AKA
Policy

Motivation
Spesso vi sono algoritmi multipli che permettono di portare ad una stessa soluzione. Non è saggio fissare questi
algoritmi interno alle classi che li richiedono, questo per diversi motivi:
- Il client probabilmente non userà tutti gli algoritmi che noi forniamo
- È difficile aggiungere nuovi algoritmi
- Inserire l’algoritmo direttamente all’interno del cliente rende più pesante il codice
Perciò, si decide di incapsulare tutti gli algoritmi sotto un unica classe. Ogni algoritmo sarà ora una strategy.

Structure

- Strategy (Compositor): declares an interface common to all supported algorithms, context uses this interface to
call the algorithm defined by a ConcreteStrategy.
- ConcreteStrategy (SimpleCompositor, TeXCompositor, ArrayCompositor): implements the algorithm using the
Strategy interface.
- Context (Composition): is configured with a ConcreteStrategy object, maintains a reference to a Strategy object,
may define an interface that lets Strategy access its data.

20

EM
Sviluppo Applicazioni Software
Pattern GRASP & Design Pattern

Example of use
Consideriamo un algoritmo che è in grado di spezzare uno stream di testo in linee separate.

La classe Composition è responsabile del mantenimento e dell’aggiornamento delle linee del testo spezzato. I vari
algoritmi non sono implementati nel Composition ma appaiono come singoli algoritmi e come implementazione di
un’interfaccia Compositor. Sono quindi strategy (SimpleCompositor, TeXCompositor e ArrayCompositor).

Consequences
✓ Gli algortimi sono meglio classificati.
✓ Si tratta di un’alternativa al subclassing. Avremmo potuto creare direttamente delle sottoclassi al Composition con
i vari algoritmi, ma in questo modo avremmo mischiato l’implementazione dell’algoritmo con quella del
Composition e non si sarebbe potuto cambiare in modo dinamico l’algoritmo.
✓ Vengono eliminate istruzioni condizionali (a seconda della strategia si chiama un metodo diverso).
✓ Le strategie possono offrire diverse implementazioni di uno stesso ambiente e quindi il cliente può facilmente
scegliere quale implementazione preferisci in termini di tradeoff.
- La comunicazione fra Composition e le sue strategy può avere dell’hoverhead: non tutte le informazioni utili ad un
algoritmo sono utili ad un altro algoritmo.
- Ci sono più oggetti: supponendo la presenza di molti algoritmi, avremo molti oggetti in più. Per diminuire questo
overhead è possibile cercare di strutturare le strategie in modo che siano condivisibili da più contesti differenti
(ovviamente laddove questo sia possibile).

Strategy vs Decorator
Qualora dovessimo avere una ConcreteComponent e un Decorator molto diversi, è possibile usare una Strategy a
più livelli ovvero ogni interfaccia della strategia apparirà come una decorazione (quindi con un puntatore alla
strategia successiva). Questa implementazione porta però altri problemi, ad esempio l’ordine di decorazione sarà
obbligato.

21

EM
Sviluppo Applicazioni Software - Capitolo 1
Pattern GRASP & Design Pattern

4.3) Visitor

Intent
Rappresentare un metodo che deve essere eseguito gli elementi di un oggetto.
Il visitor ci permette di definire nuovi metodi senza cambiare le classi degli elementi su cui esso opera.

Motivation
Prendiamo un sistema complesso che deve effettuare operazioni differenti su un input (ad esempio un
compilatore). L’input verrà organizzato in un albero e sull’albero potremo effettuare quelle operazioni di cui
abbiamo parlato sopra.
Quindi ogni nodo dell’albero potrà essere valutato come nodo di tipo 1, di tipo 2, e i metodi per ogni tipologia di
nodo faranno cose diverse (ma ovviamente avranno lo stesso nome).
Vogliamo poter aggiungere tipologie di nodi senza intaccare la struttura precedentemente costruita.

Structure

- Visitor (NodeVisitor): declares a Visit operation foreach class of ConcreteElement in the object structure. The
operation's name and signature identifies the class that sends the Visit request to the visitor. That lets the visitor
determine the concrete class of the element being visited. Then the visitor can access the element directly through
its particular interface.

22

EM
Sviluppo Applicazioni Software
Pattern GRASP & Design Pattern

- ConcreteVisitor (TypeCheckingVisitor): implements each operation declared by Visitor. Each operation implements
a fragment of the algorithm defined for the corresponding class of object in the structure. ConcreteVisitor provides
the context for the algorithm and stores its local state. This state often accumulates results during the traversal of
the structure.
- Element (Node): defines an Accept operation that takes a visitor as an argument.
- ConcreteElement (AssignmentNode,VariableRefNode): implements an Accept operation that takes a visitor as an
argument.
- ObjectStructure (Program): can enumerate its elements, may provide a high-level interface to allow the visitor to
visit its elements., may either be a composite or a collection such as a list or a set.

Example of use
Prendiamo il compilatore di cui abbiamo parlato prima.
Rappresenterà un programma in un sintax tree. Vi saranno operazioni da effettuare sull’albero e saranno sia
semantiche che sintattiche. Sarà anche necessario generare codice.
Possiamo immaginare metodi per type-checking, code optimization, flow analisys, controllo sull’assegnamento
delle variabili, ecc. Allo stesso tempo vorremo usare l’albero per effettuare stampe, generare codice, e valutare vari
aspetti del programma che si sta compilando.
Ecco come apparirà (parte) di un nodo dell’albero sintattico:
Il problema è chiaro, distribuire tutte queste operazioni sulle
varie classi diventa difficile da leggere, da capire e da
mantenere.

Non è buono avere codice per generare codice nello stesso


luogo dove v’è anche il codice per generare una stampa.
Sarebbe bello poter aggiungere ogni operazione
separatamente e che la classe nodo fosse indipendente dalle
operazioni che vi si applicano.

Perciò, “impacchetteremo” le operazioni correlate fra loro


prendendole in una classe ad hoc, chiamata visitor.
Il visitor verrà passato agli elementi dell’albero sintattico
mentre l’albero stesso viene percorso. Quando un elemento “accetta” un visitor manda una richiesta al visitor che
codifica la classe dell’elemento. Durante questa chiamata viene incluso l’elemento come parametro.
Il vistor quindi eseguirà l’operazione per l’elemento che ha ricevuto come argomento e l’operazione eseguita sarà
proprio quella che c’era (nella versione senza pattern) nella classe dell’elemento.

Quindi per esempio un compilatore senza visitor avrebbe effettuato type-check chiamando l’operazione
TypeCheck() sul suo abstract tree. Dopodiché ogni nodo avrebbe chiamato il metodo TypeCheck() sulla sua
componente.
Se invece il compilatore usasse i visitors allora prima di tutto si sarebbe creata una classe TypeCheckingVisitor.
Dunque un oggetto di tipo TypeCheckingVisitor verrebbe istanziato e verrebbe chiamata l’operazione di Accept()
sull’abstract tree passando come parametro l’istanza di TypeCheckingVisitor. Ogni nodo implementerebbe quindi il
metodo Accept() richiamando il visitor: quindi un nodo di assegnamento (ad esempio) chiamerà VisitAssignment sul
visitor, mentre un riferimento a variabile chiamerà VisitVariabileReference.
Ovvero nell’ambito del type-checking potremo effettuare sia le operazioni per i nodi di assegnamento (tramite il
metodo VisitAssignment che su riferimenti a variabile (VisitVariabileReference). I due metodi saranno inclusi dentro
alla sola classe TypeCheckingVisitor e quindi ogni nodo chiamerà il metodo giusto sulla classe passata a monte
dell’albero.

23

EM
Sviluppo Applicazioni Software - Capitolo 1
Pattern GRASP & Design Pattern

Ciò che era l’operazione TypeCheck nella classe AssignmentNode è ora il metodo VisitAssignment nella classe
TypeCheckingVisitor mentre ciò che era l’operazione TypeCheck nella classe VariabileRefNode è ora il metodo
VisitVariabileReference.

In definitiva sarà il metodo Accept di ogni nodo a scegliere quale metodo del visitor sfruttare.

Per far sì che il visitor funzioni anche per altro oltre che per il type-checking, abbiamo bisogno di una classe che
wrappi tutti i tipi di vistor, la NodeVisitor. La NodeVisitor deve dichiarare un metodo per ogni tipologia di nodo
(perché ogni nodo si chiamerà nell’Accept, la tipologia che gli interessa).

Ed ecco il nostro albero (program) con i visitor:

Come vediamo il tipo di visitor usato è sempre NodeVisitor, poi, a seconda di cosa ci interessa fare passeremo una
istanza che è TypeCheckingVisitor oppure CodeGeneratingVisitor. Ma tutte e due le tipologie avranno i metodi
VisitAssignement e VisitVariabileRef, ovvero tutte le tipologie di nodi possibili. Ecco alcune chiamate:

24

EM
Sviluppo Applicazioni Software
Pattern GRASP & Design Pattern

È chiaro che abbiamo definito due gerarchie una per gli elementi dell’albero (la gerarchia dei Node) e una per il
tipo di visita che è possibile effettuare (gerarchia NodeVisitor).
L’aggiunta di una nuova tipologia di visita consta nell’aggiunta di una classe che implementi NodeVisitor (e che
quindi abbia i metodi per tutte le tipologie di nodi).
L’aggiunta di tipo di nodo è però complessa: oltre ad (ovviamente) aggiungere il nodo nodo come sottoclasse di
Node, sarà necessario aggiornare tutti i visitor fornendo un metodo per quel tipo di nodo.

Consequences
✓ È facile aggiungere nuove visite.
✓ Le operazioni simili sono ora radunate sotto un unico visitor.
✓ I visitor possono accumulare stati o informazioni sui nodi: senza visitor avremmo dovuto passarli come parametri.
- Aggiungere una nuova tipologia di nodo è costoso (vedi sopra).
- C’è rischio di rottura dell’encaptulation. Si assume che l’interfaccia del ConcreteElement sia sufficientemente
“potente”, ovvero, esponga a sufficienza gli elementi utili per permettere al visitor di fare il suo lavoro. Quindi il
pattern costringe a rivelare parte dello stato interno di un elemento, compromettendone l’encapsulation.

25

EM
Sviluppo Applicazioni Software - Capitolo 1
Pattern GRASP & Design Pattern

5) Pattern non GoF


Parliamo ora di due pattern che non sono stati ideati dalla GoF ma sono comunque interessanti. Entrambi sono
definibili come “Creational Patterns”.

5.1) Static Factory


Si tratta di una classe molto semplice che raggruppa politiche di creazione differenti. Funge da “raccoglitore” di
metodi create in sostanza.

StaticFactory
create1()
create2()

Chiaramente i metodi create saranno metodi statici.

5.2) Concrete Factory


La concrete factory permette di creare un’oggetto astraendo dalla sua implementazione.

FactoryG

create()
Graph
Graph(FactoryG fg)

FactoryGAdjList FactoryGAdjMatrix

create() create()

Il client adopererà un oggetto di tipo Graph.


Il suo costruttore prenderà un oggetto di tipo FactoryG opportunamente istanziato a seconda dell’implementazione
desiderata (quindi, nell’esempio, grafo con liste di adiacenza piuttosto che grafo con matrici di adiacenza).
Da ciò se ne deduce che il metodo create non potrà essere statico (il binding dinamico non funziona) e dovrà essere
abstract.

Il costruttore di Graph non farà altro che chiamare la create dell’oggetto FactoryG passato come parametro.

26

EM

Anda mungkin juga menyukai