Ingegneria del Software
Design Pattern GoF Esempi su studi di caso del Larman
Prof. Orazio Tomarchio
O. Tomarchio Ingegneria del Software 2
Cosa vedremo in questa lezione
Alcuni pattern GoF applicati nel contesto del caso di studio POS NextGen analizzato durante il corso
O. Tomarchio Ingegneria del Software 3
Adapter
Riprendiamo problema e soluzione affrontati Problema
Come gestire interfacce incompatibili, o fornire una interfaccia stabile a componenti simili con interfacce diverse?
Soluzione Convertire l'interfaccia originale di un componente in un'altra
interfaccia, attraverso un oggetto adattatore intermedio
O. Tomarchio Ingegneria del Software 4
Adapter: caso d'uso POS
Il sistema POS NextGen deve supportare diversi tipi di servizi prodotti da terze parti Calcolatori delle imposte, servizi di autorizzazione ai pagamenti,
sistemi di accounting, etc Come fare?
applicare il pattern Adapter Cioè, aggiungere un livello di indirezione con oggetti che adattano le
interfacce esterne variabili ad una interfaccia compatibile utilizzata all'interno dell'applicazione
O. Tomarchio Ingegneria del Software 5
Adapter: caso d'uso POS
TaxMasterAdapter
getTaxes( Sale ) : List of TaxLineItems
GoodAsGoldTaxProAdapter
getTaxes( Sale ) : List of TaxLineItems
«interface»ITaxCalculatorAdapter
getTaxes( Sale ) : List of TaxLineItems
Adapters use interfaces and polymorphism to add a level of indirection to varying APIs in other components.
SAPAccountingAdapter
postReceivable( CreditPayment )postSale( Sale )...
GreatNorthernAccountingAdapter
postReceivable( CreditPayment )postSale( Sale )...
«interface»IAccountingAdapter
postReceivable( CreditPayment )postSale( Sale )...
«interface»IInventoryAdapter
...
«interface»ICreditAuthorizationService
Adapter
requestApproval(CreditPayment,TerminalID, MerchantID)...
O. Tomarchio Ingegneria del Software 6
Adapter: caso d'uso POS
:Register : SAPAccountingAdapter
postSale( sale )
makePayment
the Adapter adapts to interfaces in other components
SOAP over HTTP
xxx
...
«actor»: SAPSystem
O. Tomarchio Ingegneria del Software 7
(Semplificazione di) Factory
Semplificazione di Abstract Factory Problema
Chi deve essere responsabile della creazione di oggetti quando ci sono delle considerazione speciali, come ad es. una logica di creazione complessa, quando si desidera separare le responsabilità di creazione per una migliore coesione, etc?
Soluzione Crea un oggetto Pure Fabrication chiamato Factory che gestisce la
creazione
O. Tomarchio Ingegneria del Software 8
Factory: caso d'uso POS
L'uso degli Adapter fa nascere un nuovo problema: chi li crea? E come faccio a sapere quale specifico Adapter creare? Se vengono creati da un oggetto di dominio, le responsabilità di
quest'ultimo vanno oltre la logica applicativa pura, entrano in altri interessi (creazione di componenti software)
Principio fondamentale di progettazione (architetturale) Progettare per mantenere una separazione degli interessi
cioè Separare interessi distinti in aree diverse, in modo che ciascuno
abbia uno scopo coeso L'alternativa è quindi: Factory
Viene definito un oggetto “factory” per creare gli oggetti Adapter
O. Tomarchio Ingegneria del Software 9
Factory: caso d'uso POS
Vantaggi di una Factory Separano le responsabilità delle creazioni complesse in oggetti di
supporto coesi
Nascondono la logica di creazione potenzialmente complessa
Consentono l'introduzione di strategie per la gestione della memoria che possono migliorare le prestazioni, come il caching o il riciclaggio di oggetti
O. Tomarchio Ingegneria del Software 10
Factory: caso d'uso POS
ServicesFactory
accountingAdapter : IAccountingAdapterinventoryAdapter : IInventoryAdaptertaxCalculatorAdapter : ITaxCalculatorAdapter
getAccountingAdapter() : IAccountingAdaptergetInventoryAdapter() : IInventoryAdaptergetTaxCalculatorAdapter() : ITaxCalculatorAdapter...
note that the factory methods return objects typed to an interface rather than a class, so that the factory can return any implementation of the interface
if ( taxCalculatorAdapter == null ) { // a reflective or datadriven approach to finding the right class: read it from an // external property
String className = System.getProperty( "taxcalculator.class.name" ); taxCalculatorAdapter = (ITaxCalculatorAdapter) Class.forName( className ).newInstance();
} return taxCalculatorAdapter;
Esempio di progettazione guidata dai dati
(parziale)
O. Tomarchio Ingegneria del Software 11
Singleton
L'uso della Factory solleva un altro problema nella progettazione: Chi crea la factory stessa e come la si accede?
Osservazioni È necessaria una sola istanza della factory Deve essere accessibile da diversi punti del codice: problema di
visibilità Soluzione (scomoda)
Passare l'istanza di ServicesFactory come parametro ovunque sia necessario
Alternativa: Uso del pattern Singleton
O. Tomarchio Ingegneria del Software 12
Singleton: caso d'uso POS
1ServicesFactory
instance : ServicesFactory
accountingAdapter : IAccountingAdapterinventoryAdapter : IInventoryAdaptertaxCalculatorAdapter : ITaxCalculatorAdapter
getInstance() : ServicesFactory
getAccountingAdapter() : IAccountingAdaptergetInventoryAdapter() : IInventoryAdaptergetTaxCalculatorAdapter() : ITaxCalculatorAdapter...
singleton static attribute
singleton static method
// static methodpublic static synchronized ServicesFactory getInstance (){if ( instance == null ) instance = new ServicesFactory()return instance}
UML notation: in a class box, an underlined attribute or method indicates a static (class level) member, rather than an instance member
UML notation: this '1' can optionally be used to indicate that only one instance will be created (a singleton)
O. Tomarchio Ingegneria del Software 13
Singleton: caso d'uso POS
:Register 1:ServicesFactory
aa = getAccountingAdapterinitialize
...
the ‘1’ indicates that visibility to this instance was achieved via the Singleton pattern
O. Tomarchio Ingegneria del Software 14
Strategy: caso d'uso POS
Problema: Fornire una logica più complessa per la determinazione del prezzo Ad esempio:
Durante un periodo può esserci uno sconto del 10% su tutte le vendite Può esserci uno sconto di 10 euro se il totale di vendita è superiore a
200 euro Possono esserci sconti per gli anziani …
Come è possibile progettare per questi algoritmi di determinazione del prezzo variabili?
O. Tomarchio Ingegneria del Software 15
Strategy
Problema Come progettare per gestire un insieme di algoritmi o politiche
variabili ma correlati?
Come progettare per consentire di modificare questi algoritmi o politiche?
Soluzione Definisci ciascun algoritmo/politica/strategia in una classe separata,
con una interfaccia comune
O. Tomarchio Ingegneria del Software 16
Strategy: caso d'uso POS
PercentDiscountPricingStrategy
percentage : float
getTotal( s:Sale ) : Money
AbsoluteDiscountOverThresholdPricingStrategy
discount : Moneythreshold : Money
getTotal( s:Sale ) : Money
«interface»ISalePricingStrategy
getTotal( Sale ) : Money
{ return s.getPreDiscountTotal() * percentage }
???PricingStrategy
...
...
{pdt := s.getPreDiscountTotal() if ( pdt < threshold ) return pdtelse return pdt discount }
O. Tomarchio Ingegneria del Software 17
Strategy: caso d'uso POS
Si creano diverse classi SalePricingStrategy, ciascuna con un metodo polimorfo getTotal
Ciascun metodo getTotal prende come parametro l'oggetto Sale, in modo che l'oggetto che determina il prezzo possa reperire le necessarie informazioni (il prezzo) dalla Sale
L'implementazione di ciascun getTotal sarà (ovviamente) diversa
Un oggetto strategia è associato a un oggetto contesto Ovvero l'oggetto a cui va applicato l'algoritmo Nell'esempio, l'oggetto di contesto è una Sale L'oggetto contesto (Sale) ha necessità di una visibilità per attributo
verso la propria strategia
O. Tomarchio Ingegneria del Software 18
Strategy: caso d'uso POS
:PercentDiscountPricingStrategys : Sale
st = getSubtotal
t = getTotal
lineItems[ i ] :SalesLineItem
t = getTotal( s )
pdt = getPreDiscountTotal
{ t = pdt * percentage }
note that the Sale s is passed to the Strategy so that it has parameter visibility to it for further collaboration
loop
O. Tomarchio Ingegneria del Software 19
Strategy: caso d'uso POS
PercentDiscountPricingStrategy
percentage : float
getTotal( Sale ) : Money
AbsoluteDiscountOverThresholdPricingStrategy
discount : Moneythreshold : Money
getTotal( Sale ) : Money
«interface»ISalePricingStrategy
getTotal( Sale ) : Money
Sale
date...
getTotal()...
1
Sale needs attribute visibility to its Strategy
pricingStrategy
getTotal(){...return pricingStrategy.getTotal( this )
}
O. Tomarchio Ingegneria del Software 20
Strategy: caso d'uso POS
Che crea la strategia? Applicare nuovamente il pattern Factory Una PricingStrategyFactory può leggere da una proprietà di sistema
(o da una sorgente di dati esterna) il nome della classe implementazione della strategia di determinazione del prezzo e quindi crearne una istanza.
:Sale
1:PricingStrategyFactory
ps =getSalePricingStrategy
:Register
makeNewSalecreate
create(percent) ps : PercentDiscountPricingStrategy
O. Tomarchio Ingegneria del Software 21
Composite: caso d'uso POS
Altro problema: Come gestire il caso di politiche di determinazione del prezzo
multiple e conflittuali? Esempio
Sconto del 20% agli anziani Sconto del 15% ai clienti abituali per vendite superiori a 400 dollari Lunedì sconto di 50 dollari sugli acquisti superiori a 500 dollari Chi compra una confezione di tè Darjeeling riceve uno sconto del 15%
su ogni cosa
C'è un modo per modificare il progetto in modo che l'oggetto Sale non sappia se si trova di fronte a una sola o a più strategie di determinazione del prezzo?
O. Tomarchio Ingegneria del Software 22
Composite
Problema Come trattare un gruppo o una struttura composta di oggetti nello
stesso modo (polimorficamente) di un oggetto non composto (atomico)?
Soluzione Definisci le classi per gli oggetti composti e atomici in modo che
implementino la stessa interfaccia
O. Tomarchio Ingegneria del Software 23
Composite: caso d'uso POS
PercentageDiscountPricingStrategy
percentage : float
getTotal ( Sale ) : Money
AbsoluteDiscountOverThresholdPricingStrategy
discount : Moneythreshold : Money
getTotal ( Sale ) : Money
«interface»ISalePricingStrategy
getTotal ( Sale ) : Money
{ return sale .getPreDiscountTotal () * percentage }
CompositePricingStrategy
add ( ISalePricingStrategy )getTotal ( Sale ) : Money
{lowestTotal = INTEGER .MAXfor each ISalePricingStrategy strat in pricingStrategies { total := strat .getTotal ( sale ) lowestTotal = min ( total , lowestTotal ) }return lowestTotal }
1 ..*
CompositeBestForCustomerPricingStrategy
getTotal ( Sale ) : Money
CompositeBestForStorePricingStrategy
getTotal ( Sale ) : Money
strategies
All composites maintain a list of contained strategies . Therefore , define a common superclass CompositePricingStrategy that defines this list (named strategies ).
Sale
date...
getTotal ()...
1
pricingStrategy
{...return pricingStrategy .getTotal ( this )}
O. Tomarchio Ingegneria del Software 24
Composite: caso d'uso POS
Sale non sa e non è interessato a sapere se la sua strategia di determinazione del prezzo è atomica o composta, poiché hanno lo stesso aspetto
Semplicemente è un oggetto che implementa l'interfaccia IsalePricingStrategy e capisce il messaggio getTotal
:CompositeBestForCustomerPricingStrategys : Sale
st = getSubtotal
t = getTotal
lineItems[ i ] :SalesLineItem
t = getTotal( s )
the Sale object treats a Composite Strategy that contains other strategies just like any other ISalePricingStrategy
x = getTotal( s )
strategies[ j ] :: ISalePricingStrategy
UML: ISalePricingStrategy is an interface, not a class; this is the way in UML 2 to indicate an object of an unknown class, but that implements this interface
{ t = min(set of all x) }
loop
loop
O. Tomarchio Ingegneria del Software 25
Observer: caso d'uso POS
Un altro requisito del sistema POS è la capacità della GUI di mostrare il totale della vendita aggiornato quando esso cambia Come fare?
Goal: When the total of the sale changes, refresh the display with the new value
Sale
total...
setTotal( newTotal )...
O. Tomarchio Ingegneria del Software 26
Observer: caso d'uso POS
Una possibile soluzione quando la Sale cambia il suo totale, l’oggetto Sale invia un
messaggio alla finestra, chiedendogli di aggiornare il totale visualizzato
Il principio di Separazione Modello-Vista sconsiglia questa soluzione gli oggetti “modello” non devono conoscere direttamente gli oggetti
“vista” – nemmeno se sono responsabili della presentazione dei loro dati
promuove Low Coupling – verso lo strato di presentazione sostiene Protected Variations – rispetto a cambiamenti della UI
O. Tomarchio Ingegneria del Software 27
Observer (Publish-Subscribe)
Problema diversi tipi di oggetti subscriber (abbonato) sono interessati ai
cambiamenti di stato o agli eventi di un oggetto publisher (editore) ciascun subscriber vuole reagire in un modo proprio quando un
publisher genera un evento il publisher vuole mantenere un accoppiamento basso verso i suoi
subscriber
Soluzione definisci un’interfaccia “subscriber” o “listener” (ascoltatore) i subscriber implementano questa interfaccia il publisher può registrare dinamicamente i subscriber che sono
interessati ai suoi eventi, ed avvisarli quando si verifica un evento
O. Tomarchio Ingegneria del Software 28
Observer: esempio
«interface»PropertyListener
onPropertyEvent( source, name, value )
SaleFrame1
onPropertyEvent( source, name, value )
initialize( Sale sale )...
javax.swing.JFrame
...setTitle()setVisible()...
{ if ( name.equals("sale.total") ) saleTextField.setText( value.toString() );}
Sale
addPropertyListener( PropertyListener lis )publishPropertyEvent( name, value )
setTotal( Money newTotal )...
*propertyListeners
{ total = newTotal; publishPropertyEvent( "sale.total", total ); }
{ propertyListeners.add( lis );}
{ for each PropertyListener pl in propertyListeners pl.onPropertyEvent( this, name, value ); }
{ sale.addPropertyListener( this ) ... }
O. Tomarchio Ingegneria del Software 29
Observer: esempio (passi principali)
Viene definita una interfaccia, PropertyListener con l'operazione onPropertyEvent
È definita una finestra (SaleFrame1) che implementa l'interfaccia
Quando SaleFrame1 viene inizializzata, gli viene passata l'istanza di Sale (di cui visualizza il totale)
La finestra SaleFrame1 si “registra” a Sale per la notifica degli eventi proprietà, usando addPropertyListener
Sale non conosce oggetti di tipo SaleFrame1 conosce solo oggetti che implementano l'interfaccia PropertyListener l'accoppiamento è solo con una interfaccia, non con una classe della GUI
Sale è dunque un publisher di “eventi proprietà”:
Quando il totale cambia, l'istanza itera su tutti i PropertyListener registrati, notificando ciascuno di essi
O. Tomarchio Ingegneria del Software 30
Observer: caso d'uso POS
SaleFrame si registra al publisher Sale
s : Salesf : SaleFrame1
initialize( s : Sale )
addPropertyListener( sf )
propertyListeners : List<PropertyListener>
add( sf )
O. Tomarchio Ingegneria del Software 31
Observer: caso d'uso POS
La Sale pubblica un evento proprietà a tutti i suoi subscriber
s :Sale
setTotal( total )
onPropertyEvent( s, "sale.total", total )
publishPropertyEvent( "sale.total", total )
propertylisteners[ i ] : PropertyListener
loop
O. Tomarchio Ingegneria del Software 32
Observer: caso d'uso POS
L'oggetto subscriber SaleFrame1 riceve la notifica di un evento pubblicato
: SaleFrame1
onPropertyEvent( source, name, value )
saleTextField: JTextField
setText( value.toString() )
Since this is a polymorphic operation implemented by this class, show a new interaction diagram that starts with this polymorphic version
UML notation: Note this little expression within the parameter. This is legal and consise.
O. Tomarchio Ingegneria del Software 33
Observer / Publish-Subscribe
Observer – inizialmente chiamato Publish-Subscribe un oggetto (chiamato soggetto concreto da GoF) pubblica eventi zero o più oggetti (chiamati osservatori concret da GoF) possono
abbonarsi agli eventi pubblicati da un oggetto il termine Observer evidenzia il fatto che gli abbonati stanno
osservando gli eventi
Java implementa questo pattern nel cosiddetto Delegation Event Model Il publisher delega la gestione degli eventi ai suoi “listener”
O. Tomarchio Ingegneria del Software 34
Chi è chi?
«interface»PropertyListener
onPropertyEvent( source, name, value )
SaleFrame1
onPropertyEvent( source, name, value )
initialize( Sale sale )...
javax.swing.JFrame
...setTitle()setVisible()...
Sale
addPropertyListener( PropertyListener lis )publishPropertyEvent( name, value )
setTotal( Money newTotal )...
*propertyListeners
� publishes events to observers/listeners/subscribers
� registers them when they ask to subscribe
� listens for events� observes events� subscribes to notification of events
O. Tomarchio Ingegneria del Software 35
Observer
L’esempio mostra la connessione di un oggetto non GUI a un oggetto GUI è un uso comune di Observer
gestione degli eventi generati dai widget delle interfacce grafiche – AWT, Swing, .NET
Observer può essere usato anche per gestire eventi che non sono generati da, né destinati a, interfacce grafiche
O. Tomarchio Ingegneria del Software 36
Esempio: Gestione di eventi allarme
«interface»AlarmListener
onAlarmEvent( source, time )
Beeper
onAlarmEvent( source, time )...
{ display notification dialog box }
AlarmClock
addAlarmnListener( AlarmListener lis )publishAlarmEvent( time )
setTime( newTime )...
*alarmListeners
{ time = newTime; if ( time == alarmTime ) publishAlarmEvent( time ); }
{ alarmListeners.add( lis );}
{ for each AlarmListener al in alarmListeners al.onAlarmEvent( this, time ); }
AlarmWindow
onAlarmEvent( source, time )...
javax.swing.JFrame
...setTitle()setVisible()...
ReliabilityWatchDog
onAlarmEvent( source, time )...
{ beep }
{ check that all required processes are executing normally }
O. Tomarchio Ingegneria del Software 37
Observer: discussione
Relazioni tra tipi di eventi, publisher e subscriber ci possono essere diversi tipi di eventi – quindi diverse interfacce
per ciascun tipo di evento ci possono essere diversi publisher
un publisher può essere l’editore per diversi tipi di eventi
un publisher può avere zero o più subscriber
un subscriber può essere registrato a zero o più publisher, con riferimento a diversi tipi di eventi
O. Tomarchio Ingegneria del Software 38
Eventi come oggetti
In Java e in C#, gli eventi sono oggetti ciascun oggetto evento è un contenitore di un insieme di dati di
interesse l’oggetto evento viene passato come argomento nel messaggio di
notifica dell’evento
Top Related