programmazione avanzata - unisa14/04/19 1 programmazione avanzata designpattern programmazione...

43
14/04/19 1 Programmazione Avanzata Design Pattern Programmazione Avanzata a.a. 2018-19 A. De Bonis Design Pattern Nel 1994, Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides pubblicarono un libro intitolato Design Patterns - Elements of Reusable Object- Oriented Software in cui è stato introdotto il concetto di Design Pattern nell’Object Oriented Design (OOD). Il gruppo dei quattro autori è noto con il nome di Gang of Four (GOF). Nel libro viene riportato il seguente pensiero dell’architetto Christopher Alexander: “Ciascun pattern descrive un problema che si presenta più e più volte nel nostro ambiente e poi descrive il nucleo della soluzione del problema, in modo tale che tu possa riusare questa soluzione un milione di volte, senza mai applicarla alla stessa maniera.” I quattro autori osservano che ciò che Christopher Alexander esprime riguardo ai pattern negli edifici e nelle città è vero anche quando si parla di object-oriented design pattern. Solo che le soluzioni sono descritte in termini di interfacce e oggetti invece che di muri e porte. Programmazione Avanzata a.a. 2018-19 A. De Bonis

Upload: others

Post on 24-Jan-2020

28 views

Category:

Documents


0 download

TRANSCRIPT

14/04/19

1

Programmazione AvanzataDesign Pattern

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Design Pattern

• Nel 1994, Erich Gamma, Richard Helm, Ralph Johnson e John Vlissidespubblicarono un libro intitolato Design Patterns - Elements of Reusable Object-Oriented Software in cui è stato introdotto il concetto di Design Pattern nell’Object Oriented Design (OOD).• Il gruppo dei quattro autori è noto con il nome di Gang of Four (GOF). • Nel libro viene riportato il seguente pensiero dell’architetto Christopher

Alexander: “Ciascun pattern descrive un problema che si presenta più e più volte nel nostro ambiente e poi descrive il nucleo della soluzione del problema, in modo tale che tu possa riusare questa soluzione un milione di volte, senza mai applicarla alla stessa maniera.” • I quattro autori osservano che ciò che Christopher Alexander esprime riguardo ai

pattern negli edifici e nelle città è vero anche quando si parla di object-oriented design pattern.• Solo che le soluzioni sono descritte in termini di interfacce e oggetti invece che di muri e

porte.

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

2

Design Pattern

• Forniscono schemi generali per la soluzione di problematiche ricorrenti che si incontrano durante lo sviluppo del software• Favoriscono il riutilizzo di tecniche di design di successo nello

sviluppo di nuove soluzioni• Evitano al progettista di riscoprire ogni volta le stesse cose• Permettono di sviluppare un linguaggio comune che semplifica la

comunicazione tra le persone coinvolte nello sviluppo del software

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Design Pattern

• Per definire un design pattern occorre specificare:

• Il nome del pattern. Associare dei nomi ai design pattern consente un piùelevato livello di astrazione nella fase di progettazione e facilita lacomunicazione tra gli addetti ai lavori e la documentazione.

• Il problema. Il problema descrive in quali contesti ha senso applicare ilpattern.

• La soluzione. La soluzione fornisce la descrizione astratta di un problema(nel nostro caso di OOD) e indica come utilizzare gli strumenti a disposizione (nel nostro caso classi e oggetti) per risolverlo.

• Le conseguenze. Le conseguenze descrivono i risultati dell’applicazione del design pattern. Esse sono fondamentali per valutare le diverse alternative e comprendere i costi e i benefici risultanti dall’applicazione del pattern

Programmazione Avanzata a.a. 2018-19

A. De Bonis

14/04/19

3

Design Pattern: elenco• 1.Adapter • 2.Facade • 3.Composite • 4.Decorator • 5.Bridge • 6.Singleton • 7.Proxy • 8.Flyweight • 9.Strategy • 10.State• 11.Command • 12.Observer

Programmazione Avanzata a.a. 2018-19 A. De Bonis

13. Memento 14.Interpreter 15.Iterator 16. Visitor17.Mediator 18.Template Method 19.Chain of Responsibility20.Builder 21.Prototype 22.Factory Method 23.Abstrac Factory

Design Pattern: classificazione

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

4

Programmazione AvanzataDesign Pattern Creazionali

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Design Pattern: classificazione

Programmazione Avanzata a.a. 2018-19 A. De Bonis

• I pattern creazionali riguardano il processo di creazione degli oggetti

• I pattern strutturali riguardano la composizione di classi ed oggetti

• I pattern comportamentali caratterizzano i modi in cui le classi e gli oggetti interagiscono tra di loro e si distribuiscono le responsabilità

14/04/19

5

Riferimenti

• Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides “Design Patterns - Elements of Reusable Object-Oriented Software”, Addison-Wesley

• Mark Summerfield, “Python in Practice: Create Better Programs Using Concurrency, Libraries, and Patterns ”, Addison-Wesley Professional

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Design Pattern Creazionali

• I design pattern creazionali astraggono il processo di creazione• Aiutano a rendere il sistema indipendente da come i suoi oggetti sono

creati, composti e rappresentati• I design pattern di questo tipo diventano sempre più utili man mano

che il sistema diventa sempre più dipendente dalla composizione di oggetti. Man mano che ciò accade, l’enfasi si sposta dalla codifica di un insieme fissato di comportamenti alla definizione di un insieme piùpiccolo di comportamenti fondamentali che possono essere compostiper dar vita a comportamenti più complessi

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

6

Abstract Factory Pattern• Riguarda situazioni in cui vogliamo creare oggetti complessi tutti facenti parte di una stessa

“famiglia”• Per esempio consideriamo un sistema GUI che supporta diversi standard. Il sistema fornisce le

operazioni per creare widget per tre diversi tipi di piattaforme: Mac, Xfce, Windows.

• Per essere portabile nelle varie piattaforme, un’applicazione non dovrebbe codificare i widget peruna particolare piattaforma.

• Il problema è risolto creando una classe astratta WidgetFactory che dichiara un’interfacciagenerale per creare ogni tipo di base di widget e tre classi concrete MacWidgetFactory, XfceWidgetFactory e WindowsWidgetFactory per creare widget per ciascune delle trepiattaforme.

• Ci sono anche classi astratte per ogni tipo di base di widget e sottoclassi concrete cheimplementamo i widget per le specifiche piattaforme Ogni sottoclasse concreta implementai metodi per creare gli stessi oggetti (make_button(), make_spinbox(), etc.), con uno stile appropriato alla piattaforma.

• La classe astratta WidgetFactory fornisce un’interfaccia con un’operazione che restituisce un oggetto widget per ciascuna classe astratta widget. Per usare queste operazioni è sufficienteconoscere l’interfaccia fornita da WidgetFactory e non occorre avere alcuna conoscenza delleclassi concrete.

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Abstract Factory Pattern: un esempio

• Due factory: una produce in output in formato di testo; l’altra produce un output in formato SVG (Scalable Vector Graphics).

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

7

Abstract Factory Pattern: un esempio• Nella parte di codice non mostrata vengono creati due filename:

textFilename e svgFilename• Viene creato un diagramma in formato testo (➊) che viene

successivamente salvato in textFilename• Viene quindi creato un diagramma in formato SVG (➋) che viene poi

salvato in svgFilename

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Abstract Factory Pattern: un esempio• La funzione create_diagram (invocata nel main) prende una diagram

factory come argomento e la usa per creare il diagramma.• La funzione non conosce quale tipo di factory riceve in input

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

8

Abstract Factory Pattern: un esempio• Questa è la factory di diagramma in formato testo che svolge anche il

ruolo di classe base per le altre factory• Nonostante si parli di pattern astratto, non è raro che una classe venga

utilizzata come interfaccia astratta e anche come classe concreta di per sé.

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Abstract Factory Pattern: un esempio• Questa è la factory di diagramma in formato SVG • L’unica differenza tra i metodi make_diagram() delle due classi è che

il metodo Diagram-Factory.make_diagram() restituisce un oggettoDiagramma mentre il metodo SvgDiagramFactory.make_diagram() restituisce un oggetto SvgDiagram. Lo stesso vale per i due altrimetodi di SvgDiagramFactory (non mostrati).

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

9

Abstract Factory Pattern: un esempio• Le implementazioni delle classi Diagram, Rectangle e Text usate per il

diagramma in formato di testo sono completamente differenti da quelle delle classi SvgDiagram, SvgRectangle e SvgText

• Le due classi Diagram e SvgDiagram condividono solo l’interfaccia(hanno gli stessi metodi)

• Non possiamo mischiare classi di famiglie differenti come ad esempio Rectangle e SvgText

Programmazione Avanzata a.a. 2018-19

A. De Bonis

Abstract Factory Pattern: un esempio• Questa è la classe Text. • Quando si produce un testo, fontsize non viene utilizzato• Gli oggetti Diagram (testo) mantengono i loro dati in liste di liste di

stringhe di un unico carattere, dove ogni carattere può essere unospazio, +, |, -, e così via. • Le classi Rectangle e Text contengono una lista di liste di stringhe di

singoli caratteri che andranno a sostituire quelle nel diagramma nellaposizione corrispondente (da sinistra verso destra e dall’alto verso ilbasso)

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

10

Abstract Factory Pattern: un esempio• Questa è la classe Diagram• Viene mostrato solo il metodo Diagram.add()• Quando component è un oggetto Rectangle o Text, questo metodo itera su

tutti i caratteri che compaiono nella lista delle liste di stringhe di un unicocarattere (memorizzate in component.rows) e sostituisce i carattericorrispondenti del diagramma. • Quando Diagram(width, height) è invocato, il metodo Diagram.__init__()

(non mostrato) fa in modo che self.diagram sia una lista di liste di spazi di ampiezza width e altezza height.

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Abstract Factory Pattern: un esempio• Questa è la classe SvgText e le due costanti da cui la classe dipende• locals() restituisce un dizionario contenente le variabili definite nel namespace

locale. • L’uso di **locals() ci evita di scrivere SVG_TEXT.format(x=x, y=y, text=text,

fontsize=fontsize).

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

11

Abstract Factory Pattern: un esempio• Ciascuna istanza di SvgDiagram contiene una lista di stringhe nella

variabile self.diagram

• Ciascuna stringa è un pezzo di SVG text. Ciò rende molto facile aggiungere nuove componenti, ad esempio di tipo SvgRectangle o SvgText.

Programmazione Avanzata a.a. 2018-19

A. De Bonis

Abstract Factory Pattern: un esempio

• L’implementazione delle due factory vista prima ha dei difetti• Nessuna delle due factory ha veramente bisogno di un proprio stato e quindi

di essere istanziata.• Il codice di SvgDiagramFactory è molto simile a quello di DiagramFactory:

l’unica differenza riguarda il tipo di ritorno dei metodi.• Il namespace di primo livello contiene tutte le classi DiagramFactory, Diagram,

Rectangle, Text, e tutte le corrispondenti SVG ma noi abbiamo bisogno solo di accedere alla due factory. Inoltre siamo costretti ad usare nomi differenti per le classi per il diagramma SVG (ad esempio SvgRectangle al posto di Rectangle). Quest’ultimo problema potrebbe essere risolto mettendo ogniclasse in un modulo differente ma ciò non risolverebe il problema delladuplicazione del codice descritto al punto precedente.

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

12

Abstract Factory Pattern: un esempio

Prima modifica:

• trasformiamo le classi Diagram, Rectangle e Text in classi annidateall’interno di DiagramFactory

• ciò significa che per accedere alle classi Diagram, Rectangle e Text dobbiamo

usare il prefisso DiagramFactory (ad esempio DiagramFactory.Diagram)

• se facciamo lo stesso per le corrispondenti classi SVG, cioè le annidiamo

all’interno di SvgDiagramFactory, allora possiamo chiamare queste classi con gli stessi nomi usati per le corrispondenti classi per la versione in formato di

testo senza che vi siano problemi di conflitti tra i nomi.

• annidiamo anche tutte le costanti da cui dipendono le classi

• ora i nostri nomi nel namespace di primo livello sono soltanto main(),

create_diagram(), DiagramFactory e SvgDiagramFactory.

Programmazione Avanzata a.a. 2018-19

A. De Bonis

Abstract Factory Pattern: un esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

BLANK = " "CORNER = "+"HORIZONTAL = "-"VERTICAL = "|"

class Diagram:...

14/04/19

13

Abstract Factory Pattern: un esempio

• I metodi make_...() di DiagramFactory ora sono metodi di classe• In una chiamata a DiagramFactory.make_text() la classe passata

come argomento è DiagramFactory e il valore restituito è un oggettodi tipo DiagramFactory.Text

• Questa modifica implica anche che la sottoclasse SvgDiagramFactorydi DiagramFactory non necessita di alcun metodo make_...()• se invochiamo SvgDiagramFactory.make_rectangle(), viene di fatto invocato il

metodo della classe base DiagramFactory.make_rectangle() ma la classepassata sarà la classe SvgDiagramFactory e verrà quindi resituito un oggettoSvgDiagramFactory.Rectangle.

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Abstract Factory Pattern: un esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

• I cambiamenti fatti ci permettono di semplificare il main() dal momento che non abbiamo più bisogno di creare istanze di factory

• Il resto del codice è quasi identico a prima: la differenza piùimportante consiste nel fatto che ora le costanti e le classi non-factory sono innestate dentro le factory e di conseguenza dobbiamo accederead esse usando il nome della factory.

14/04/19

14

Abstract Factory Pattern: un esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

• Parte della classe SvgDiagramFactory• non contiene più i metodi make_...()

Builder Pattern

• Il Builder Pattern è simile all’Abstract Factory Pattern in quanto èprogettato per creare oggetti complessi che sono composti da altrioggetti• A differenza dell’Abstract Factory Pattern, il Builder Pattern non solo

fornisce i metodi per costruire oggetti complessi ma mantiene anchela rappresentazione dell’intero oggetto• Questo pattern è particolarmente adatto ai casi in cui è necessario

tenere separata la rappresentazione dell’oggetto complesso daglialgoritmi di composizione

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

15

Builder Pattern

• Il Builder Pattern fornisce una chiara separazione tra la costruzione e la rappresentazione di un oggetto

• Lo stesso processo di costruzione può creare diversi tipi di rappresentazione

• Fornisce gli strumenti per modificare lo stato interno degli oggetti

• Permette di ottenere gli oggetti creati

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Builder Pattern: un’applicazione

• Un’applicazione richiede di leggere documenti RTF (Rich Text Format) e convertirli in diversi tipi di formati (testo ASCII, Widget in formato testuale, ecc.)

• Il problema è che vogliamo aggiungere al sistema nuovi formati senzamodificare il lettore che si occupa di leggere i documenti RTF e convertirli nei diversi tipi di formati.

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

16

Builder Pattern: un’applicazione

• Una soluzione è di creare un lettore (Reader) per i documenti RTF equipaggiato con un convertitore che converte il documento RTF in un altro formato.

• Man mano che il lettore scandisce il documento RTF esso usa ilConverter per effettuare la conversione dei token scanditi.

• Gli oggetti di tipo Converter si occupano sia della conversione dei datisia della rappresentazione del token in un particolare formato

Programmazione Avanzata a.a. 2018-19

A. De Bonis

Builder Pattern: un’applicazione

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

17

Builder Pattern: un’applicazione

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Partecipanti:• Builder (Converter): specifica un’interfaccia astratta per creare le parti del prodotto• nella nostra applicazione , l’interfaccia per convertire i singoli token

• ConcreteBuilder (ASCIIConverter, PDFConverter, PostScriptConverter):• costruisce le parti del prodotto implementando l’interfaccia Builder• definisce e tiene traccia delle parti che crea• fornisce un’interfaccia per ottenere il prodotto (ad esempio, GetASCIIText, Get-

PDF).• Director (Reader): costruisce un oggetto usando l’interfaccia Builder• Product (ASCIIText, PDFText, PostScriptText): • rappresenta l’oggetto da costruire. La classe ConcreteBuilder costruisce la

rappresentazione interna del prodotto e definisce il modo in cui viene assemblato• include classi che rappresentano le singole parti da cui è formato il prodotto

Builder Pattern: interazione tra i partecipanti

Programmazione Avanzata a.a. 2018-19 A. De Bonis

• Il programma crea un oggetto Director configurato con l’oggetto Builder desiderato• Director usa il builder ogni volta che deve essere creata una parte del

prodotto• Builder gestisce le richieste provenienti dal director e aggiunge parti al

prodotto. • Il programma ottiene il prodotto dal Builder.

14/04/19

18

Builder Pattern: un semplice esempio• versione modificata del codice disponibile all’indirizzo

https://gist.github.com/pazdera/1121157

• Si vogliono costruire diversi tipi di auto

Programmazione Avanzata a.a. 2018-19 A. De Bonism

Builder Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class Director:""" Controlla il processo di produzione.Director ha un builder associato e delega la costruzione delleparti più piccole al builder e poi le assembla (in car)."""__builder = Nonedef setBuilder(self, builder):

self.__builder = builder# L’algoritmo che assembla le parti dell’autodef getCar(self):

car = Car()body = self.__builder.getBody() # Prima la carrozzeriacar.setBody(body)engine = self.__builder.getEngine() # poi il motorecar.setEngine(engine)i = 0 # le 4 ruotewhile i < 4:

wheel = self.__builder.getWheel()car.attachWheel(wheel)i += 1

return car

14/04/19

19

Builder Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class Car:""" Il prodotto finale: l’auto. L’auto è assemblatadal ‘Director’ e le sue parti costruite dal Builder."""def __init__(self):

self.__wheels = list()self.__engine = Noneself.__body = None

def setBody(self, body):self.__body = body

def attachWheel(self, wheel):self.__wheels.append(wheel)

def setEngine(self, engine):self.__engine = engine

def specification(self):print "body: %s" % self.__body.shapeprint "engine horsepower: %d" % self.__engine.horsepowerprint "tire size: %d\'" % self.__wheels[0].size

Builder Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class Builder(metaclass=abc.ABCMeta): :

""" Interfaccia per creare le varie parti del veicolo

"""

@abc.abstractmethoddef getWheel(self): pass

@abc.abstractmethoddef getEngine(self): pass

@abc.abstractmethoddef getBody(self): pass

NB: deve essere importato abc

14/04/19

20

Builder Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class JeepBuilder(Builder):""" Classe concreta che costruisce le parti di una Jeep implementando l’interfaccia Builder. Definisce e tiene traccia della rappresentazione che crea. """def getWheel(self):

wheel = Wheel()wheel.size = 22return wheel

def getEngine(self):engine = Engine()engine.horsepower = 400return engine

def getBody(self):body = Body()body.shape = "SUV"return body

Builder Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class NissanBuilder(Builder):""" Classe concreta che costruisce le parti di una Nissanimplementando l’interfaccia Builder.

..."""def getWheel(self):

wheel = Wheel()wheel.size = 16return wheel

def getEngine(self):engine = Engine()engine.horsepower = 85return engine

def getBody(self):body = Body()body.shape = "hatchback"return body

14/04/19

21

Builder Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

# Le parti dell’autoclass Wheel:

size = None

class Engine:horsepower = None

class Body:shape = None

Builder Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

def main():jeepBuilder = JeepBuilder()nissanBuilder = NissanBuilder()director = Director()

# costruisce la Jeepprint "Jeep"director.setBuilder(jeepBuilder)jeep = director.getCar()jeep.specification()

print ""

# costruisce la Nissanprint "Nissan"director.setBuilder(nissanBuilder)nissan = director.getCar()nissan.specification()

if __name__ == "__main__":main()

Sign up for free

14/04/19

22

Factory Method Pattern

• Si usa quando vogliamo definire un’interfaccia per creare degli oggettie delegare le sue sottoclassi a decidere quale classe istanziare quandoviene richiesto un oggetto. • Particolarmente utille quando una classe non può conoscere in

anticipo la classe degli oggetti che deve creare

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Factory Method Pattern: un’applicazione

• Esempio: Consideriamo un framework per delle applicazioni ciascunadelle quali elabora documenti di diverso tipo. • Abbiamo bisogno di due astrazioni: la classe Application e la classe Document • Entrambe le classi sono astratte e occorre derivare delle loro sottoclassi per

poter realizzare le implementazioni relative a ciascuna applicazione• Ad esempio, per creare un’applicazione per disegnare, definiamo le classi

DrawingApplication e DrawingDocument. La classe Application gestisce idocumenti e li crea su richiesta dell’utente• Ad esempio, quando l’utente seleziona Open o New dal menu.

• Definiamo un’interfaccia per creare un oggetto ma lasciamo alle sottoclassidecidere quali classi istanziare.

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

23

Factory Method Pattern: un’applicazione• Poiché la particolare sottoclasse di Document da istanziare dipende

dalla particolare applicazione, la classe Application non può fare previsioni riguardo alla sottoclasse di Document da istanziare

• La classe Application sa solo quando deve essere creato un nuovodocumento ma non ne conosce il tipo.

• Problema: devono essere istanziate delle classi ma si conoscono solodelle classi astratte che non possono essere istanziate

• Il Factory method pattern risolve questo problema incapsulandol’informazione riguardo alla sottoclasse di Document da creare e

sposta questa informazione all’esterno.

Programmazione Avanzata a.a. 2018-19

A. De Bonis

Factory Method Pattern: un’applicazione

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

24

Factory Method Pattern: un’applicazione

Programmazione Avanzata a.a. 2018-19 A. De Bonis

• Le sottoclassi di Application ridefiniscono il metodo astrattoCreateDocument per restituire la sottoclasse appropriata di Document

• Una volta istanziata, la sottoclasse di Application può creare istanze di Document per specifiche applicazioni senza dover conoscere lesottoclassi delle istanze create (CreateDocument)

• CreateDocument e` detto factory method perche’ e` responsabiledella creazione degli oggetti

Factory Method Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class Pizza():def __init__(self):

self._price = None

def get_price(self):return self._price

14/04/19

25

Factory Method Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class HamAndMushroomPizza(Pizza):def __init__(self):

self._price = 8.5

class DeluxePizza(Pizza):def __init__(self):

self._price = 10.5

class HawaiianPizza(Pizza):def __init__(self):

self._price = 11.5

Factory Method Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class PizzaFactory:@staticmethoddef create_pizza(pizza_type):

if pizza_type == 'HamMushroom':return HamAndMushroomPizza()

elif pizza_type == 'Deluxé:return DeluxePizza()

elif pizza_type == 'Hawaiian':return HawaiianPizza()

• PizzaFactory fornisce il metodo createPizza che è statico per cui può essere invocato quando non è stata ancora creata una pizza

14/04/19

26

Factory Method Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

if __name__ == '__main__':for pizza_type in ('HamMushroom', 'Deluxé, 'Hawaiian'):

print('Price of {0} is {1}'.format(pizza_type, PizzaFactory.create_pizza(pizza_type).get_price())

• il tipo di pizza avrebbe potuto essere fornito dall’utente • il tipo di pizza indicato dall’utente potrebbe essere stato inserito successivamente nel

menu e la classe concreta corrispondente creata successivamente al main• occorre modificare solo la factory

Factory Method Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19

A. De Bonis

• Che cosa accade se vogliamo creare diversi tipi di negozi ciascuno dei quali vendepizze nello stile di una certà città

• Creiamo una classe astratta PizzaStore al cui interno c’è il metodo astrattocreate_pizza

• Dalla classe PizzaStore deriviamo NYPizzaStore, ChicagoPizzaStore e così via. Questesottoclassi sovrascriveranno il metodo astratto. La decisione sul tipo di pizza dacreare è presa dal metodo create_pizza dellaspecifica sottoclasse.• analogamente a quanto accadeva nel framework per la gestione dei documenti

• PizzaStore avrà anche un metodo orderPizza() che invoca createPizza ma non ha idea su quale pizza verrà creata fino a che non vera creata una classe concreta di PizzaStore

14/04/19

27

Factory Method Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

from abc import ABC, abstractmethod

class Pizza(ABC):

@abstractmethoddef prepare(self):

pass

def bake(self):print("baking pizza for 12min in 400 degrees..")

def cut(self):print("cutting pizza in pieces")

def box(self):print("putting pizza in box")

Factory Method Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class NYStyleCheesePizza(Pizza):def prepare(self):

print("preparing a New York style cheese pizza..")

class ChicagoStyleCheesePizza(Pizza):def prepare(self):

print("preparing a Chicago style cheese pizza..")

class NYStyleGreekPizza(Pizza):def prepare(self):

print("preparing a New York style greek pizza..")

class ChicagoStyleGreekPizza(Pizza):def prepare(self):

print("preparing a Chicago style greek pizza..")

14/04/19

28

Factory Method Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class PizzaStore(ABC):

@abstractmethoddef _createPizza(self, pizzaType: str) -> Pizza:

pass

def orderPizza(self, pizzaType):pizza: Pizza

pizza = self._createPizza(pizzaType)

pizza.prepare()pizza.bake()pizza.cut()pizza.box()

Factory Method Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class NYPizzaStore(PizzaStore):

def _createPizza(self, pizzaType: str) -> Pizza:pizza: Pizza = None

if pizzaType == 'Greek’:pizza = NYStyleGreekPizza()

elif pizzaType == 'Cheese’:pizza = NYStyleCheesePizza()

else:print("No matching pizza found in the NY pizza store...")

r eturn pizza

14/04/19

29

Factory Method Pattern: un semplice esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class ChicagoPizzaStore(PizzaStore):

def _createPizza(self, pizzaType: str) -> Pizza:pizza: Pizza = None

if pizzaType == 'Greek’:pizza = ChicagoStyleGreekPizza()

elif pizzaType == 'Cheese’:pizza = ChicagoStyleCheesePizza()

else:print("No matching pizza found in the Chicago pizza store...")

return pizza

Factory Method Pattern: un esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Voglio creare una scacchiera per la dama ed una per gli scacchi

14/04/19

30

Factory Method Pattern: un esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

• la scacchiera è una lista di liste (righe) di stringhe di un singolo carattere• __init__ Inizializza la scacchiera con tutte le posizioni vuote e poi invoca populate_board per inserire i

pezzi del gioco• populate_board è astratto

Factory Method Pattern: un esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

• La classe per scacchiere per il gioco della dama

14/04/19

31

Factory Method Pattern: un esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

• La classe per scacchiere per il gioco degli scacchi

Factory Method Pattern: un esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

• La classe base per i pezzi

• Si è scelto di creare una classe che discende da str invece che direttamente str per poter facilmente testare se un oggetto z è unpezzo del gioco con isinstance(z,Piece)

• ponendo __slots__={} ci assicuriamo che gli oggetti di tipo Piece nonabbiano variabili di istanza

14/04/19

32

__slots__• In Python ogni istanza di una classe ha un dizionario

(__dict__) che memorizza gli attributi

• Spreco di spazio se la classe ha pochi attributi • Problema aggravato se si creano tante istanze della classe

• Si può modificare il comportamento di default definendo __slots__ quando si definisce una classe

Programmazione Avanzata a.a. 2018-19 Docente: A. De Bonis

63

__slots__• A __slots__ si assegna una sequenza di variabili di

istanza in modo che venga riservato, in ogni istanza della classe, solo lo spazio sufficiente a memorizzare un valore per ogni variabile• __dict__ non sarà più creato• non sono più possibili assegnamenti dinamici

Programmazione Avanzata a.a. 2018-19 Docente: A. De Bonis 64

class MyNewClass:__slots__ = 'L'

. . .

var_c = MyClass(5, 6)print(var_c.__dict__)var_cn = MyNewClass(5, 6)print(var_cn.__slots__)

{'L': [5, 6]}

L

14/04/19

33

Factory Method Pattern: un esempio

Programmazione Avanzata a.a. 2018-19

A. De Bonis

• La classe pedina nera

• le classi per gli altri pezzi sono create in modo analogo

• Ognuna di queste classi è una sottoclasse immutabile di Piece che è

sottoclasse di str

• Inizializzata con la stringa di un unico carattere (il carattere Unicode che

rappresenta il pezzo)

class BlackDraught(Piece):

__slots__ = ()

def __new__(Class):

return super().__new__(Class, "\N{black draughts man}")

__new__ e __init__

• __new__ crea un oggetto• __init__ inizializza le variabili dell’istanza• quando viene creata un’istanza di una classe viene invocato prima

__new_ e poi _init__• __new__ accetta cls come primo parametro perché quando viene

invocato di fatto l’istanza deve essere ancora creata• __init_ accetta self come primo parametro

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

34

__new__ e __init__

• tipiche implementazioni di __new_ creano una nuova istanza della classe clsinvocando il metodo __new__ della superclasse con super(currentclass, cls).__new__(cls,...) . Tipicamente prima di restituirel’istanza __new__ modifica l’istanza appena creata.• Se __new__ restituisce un’istanza di cls allora viene invocato il metodo

__init__(self,...), dove self è l’istanza creata e i restanti argomenti sono gli stessipassati a __new__• Se __new__ non restituisce un’istanza allora __init__ non viene invocato.• __new__ viene utilizzato soprattutto per consentire a sottoclassi di tipi

immutabili (come ad esempio str, int e tuple) di modificare la creazione delleproprie istanze.

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Factory Method Pattern: un esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

• Questa nuova versione del metodo CheckersBoard.populate_board() è un factory method in quanto dipende dalla funzione factory new create_piece() • Nella versione precedente il tipo di pezzo era indicato nel codice• La funzione create_piece() restituisce un oggetto del tipo appropriato (ad

esempio, BlackDraught o WhiteDraught) in base ai suoi argomenti.• Il metodo ChessBoard.populate_board() viene anch’esso modificato in modo da

usare la stessa funzione create_piece() invocata qui.

14/04/19

35

Factory Method Pattern: un esempio

Programmazione Avanzata a.a. 2018-19 A. De Bonis

• Questa funzione factory usa la funzione built-in eval() per creareistanze della classe• Ad esempio se gli argomenti sono "knight" and "black", la stringa

valutata sarà "BlackChessKnight()". • In generale è meglio non usare eval per eseguire il codice

rappresentato da un’espressione perché è potenzialmente rischiosodal momento che permette di eseguire il codice rappresentato da unaqualsiasi espressione

Il pattern Prototype

• Il pattern Prototype è usato per creare nuovi oggetti clonando un oggetto preesistente e poi modificando il clone così creato.

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

36

Il pattern Prototype: esempio

• Point è la classe preesistente

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Il pattern Prototype: esempio• In questo codice cloniamo nuovi punti in diversi modi

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

37

Il pattern Prototype: esempio• point1 = Point(1, 2)

• viene semplicemente invocato il costruttore della classe Point

• point2 = eval("{}({}, {})".format("Point", 2, 4))

• usa eval() per creare istanze di Point• eval valuta l’espressione Python rappresentata dalla stringa ricevuta come

argomento. In altre parole, esegue il codice passato come argomento

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Il pattern Prototype: esempio• point3 = getattr(sys.modules[__name__], "Point")(3, 6)

• usa la funzione built-in getattr() per creare un’istanza

• getattr(object,name, default) restituisce il valore dell’attributo dell’oggetto• object : oggetto per il quale viene restituito il valore dell’attributo di nome name• name : stringa che contiene il nome dell’attributo• default (opzionale): valore restituito quando l’attributo specificato non viene trovato

• sys.modules è un dizionario che contiene coppie del tipo (nome-modulo, modulo), dove modulo è un modulo già caricato

• l’espressione sys.modules[__name__] restituisce il modulo in cui essa si trova• l’espressione getattr(sys.modules[__name__], "Point") restituisce il valore

dell’attributo Point del modulo

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

38

Il pattern Prototype: esempio• point4 = globals()["Point"](4, 8)

• La funzione built-in globals() restituisce un dizionario che rappresenta la tabella dei simboli globali. All’interno di una funzione o un metodo, ildizionario è quello relativo al modulo dove è definita la funzione (o ilmetodo), non a quello in cui è invocata .• comportamento similie a getattr(object,name, default)

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Il pattern Prototype: esempio• point5 = make-object(Point, 3,9)

• usa la seguente funzione make-object

Programmazione Avanzata a.a. 2018-19 A. De Bonis

def make_object(Class, *args, **kwargs): return Class(*args, **kwargs)

14/04/19

39

Il pattern Prototype: esempio• Python ha un supporto built-in per creare oggetti sulla base di

prototipi: la funzione copy.deepcopy().

• point6 = copy.deepcopy(point5)• usa il classico approccio basato su Prototype:

• prima clona un oggetto esistente• poi lo inizializza con le istruzioni successive

• point7 = point1.__class__(7, 14)• point7 è creato usando point1 • instanza.__class__ contiene la classe a cui appartiene istanza

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Il pattern Singleton• Il pattern Singleton è usato quando abbiamo bisogno di una classe

che ha un’unica istanza che è la sola ad essere utilizzata dal programma.• In particolare, è utile nelle seguenti situazioni:• Controllare l’acceso concorrente ad una risorsa condivisa• Se si ha bisogno di un punto globale di accesso per la risorsa da parti

differenti del sistema.• Quando si ha bisogno di un unico oggetto di una certa classe

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

40

Il pattern SingletonAlcuni usi comuni: • Lo spooler della stampante: vogliamo una singola istanza dello

spooler per evitare il conflitto tra richieste per la stessa risorsa• Gestire la connessione ad un database• Trovare e memorizzare informazioni su un file di configurazione

esterno

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Il pattern Singleton

• Il pattern Singleton è usato quando abbiamo bisogno di una classeche ha un’unica istanza che è la sola ad essere utilizzata dal programma• In python creare un singleton è un’ operazione molto semplice• Il Python Cookbook (trasferito presso GitHub.com/activestate/code)

fornisce• una classe Singleton di facile uso. Ogni che discende da essa diventa un

singleton• una classe Borg che ottiene la stessa cosa in modo differente

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

41

Il pattern Singleton: la classe Singletonclass Singleton:

class __impl:""" Implementation of the singleton interface """

def spam(self):""" Test method, return singleton id """return id(self)

# storage for the instance reference__instance = None

Programmazione Avanzata a.a. 2018-19 A. De Bonis

Il pattern Singleton: la classe Singletondef __init__(self):

""" Create singleton instance """

# Check whether we already have an instanceif Singleton.__instance is None:

# Create and remember instanceSingleton.__instance = Singleton.__impl()

# Store instance reference as the only member in the handleself.__dict__['_Singleton__instancé] = Singleton.__instance

Programmazione Avanzata a.a. 2018-19 A. De Bonis

14/04/19

42

Il pattern Singleton: la classe Singleton

def __getattr__(self, attr):""" Delegate access to implementation """return getattr(self.__instance, attr)

def __setattr__(self, attr, value):""" Delegate access to implementation """return setattr(self.__instance, attr, value)

# Test its1 = Singleton()print (id(s1), s1.spam())

s2 = Singleton()print (id(s2), s2.spam())

Programmazione Avanzata a.a. 2018-19 A. De Bonis

invoca __get__attr__ definito in singleton

Il pattern Singleton: la classe Borg• Nella classe Borg tutte le istanze sono diverse ma condividono lo stesso stato. • Nel codice in basso, lo stato condiviso è nell’attributo _shared_state e tutte le

nuove istanze di Borg avranno lo stesso stato così come è definito dal metodo__new__ .• In genere lo stato di un’istanza è memorizzato nel dizionario __dict__ proprio

dell’istanza. Nel codice in basso assegnamo la variabile di classe _shared_state a tutte le istanze create

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class Borg(): _shared_state = {} def __new__(cls, *args, **kwargs):

obj = super(Borg, cls).__new__(cls, *args, **kwargs) obj.__dict__ = cls._shared_statereturn obj

14/04/19

43

Il pattern Singleton: la classe Borg• creiamo istanze diverse di Borg: borg e another_borg• creiamo un’istanza della sottoclasse Child di Borg• aggiungiamo la variabile di istanza only_one_var a borg• siccome lo stato è condiviso da tutte le istanze di Borg, anche child avrà la variabile di istanza

only_one_var

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class Child(Borg):pass

>>> borg = Borg()>>> another_borg = Borg()>>> borg is another_borgFalse>>> child = Child()>>> borg.only_one_var = "I'm the only one var”>>> child.only_one_varI'm the only one var

Il pattern Singleton: la classe Borg• Se vogliamo definire una sottoclasse di Borg con un altro stato condiviso

dobbiamo resettare _shared_state come segue

Programmazione Avanzata a.a. 2018-19 A. De Bonis

class AnotherChild(Borg):_shared_state = {}

>>> another_child = AnotherChild()>>> another_child.only_one_varAttributeError: AnotherChild instance has no attribute 'shared_staté