19
Ingegneria del Software Design Paern GoF Esempi su studi di caso del Larman Prof. Orazio Tomarchio O. Tomarchio Ingegneria del Software 2 Cosa vedremo in questa lezione Alcuni paern GoF applica nel contesto del caso di studio POS NextGen analizzato durante il corso

Design Pattern GoF - studium.unict.it

  • Upload
    others

  • View
    17

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Design Pattern GoF - studium.unict.it

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

Page 2: Design Pattern GoF - studium.unict.it

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

Page 3: Design Pattern GoF - studium.unict.it

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

Page 4: Design Pattern GoF - studium.unict.it

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

Page 5: Design Pattern GoF - studium.unict.it

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 data­driven 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)

Page 6: Design Pattern GoF - studium.unict.it

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) 

Page 7: Design Pattern GoF - studium.unict.it

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?

Page 8: Design Pattern GoF - studium.unict.it

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 }

Page 9: Design Pattern GoF - studium.unict.it

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

Page 10: Design Pattern GoF - studium.unict.it

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

Page 11: Design Pattern GoF - studium.unict.it

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

Page 12: Design Pattern GoF - studium.unict.it

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

Page 13: Design Pattern GoF - studium.unict.it

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

Page 14: Design Pattern GoF - studium.unict.it

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 )  ...   }

Page 15: Design Pattern GoF - studium.unict.it

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 )

Page 16: Design Pattern GoF - studium.unict.it

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. 

Page 17: Design Pattern GoF - studium.unict.it

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

Page 18: Design Pattern GoF - studium.unict.it

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 }

Page 19: Design Pattern GoF - studium.unict.it

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