Python: observer pattern per programmatori di coccio
Che cos’è?
Da wikipedia:
L’Observer pattern è un design pattern (potremmo tradurlo in schema progettuale)
utilizzato per tenere sotto controllo lo stato di diversi oggetti (Subject).
Lo schema UML (Unified Modeling Language) è il seguente:
+-------------------+ | Observer |<--------[ observer 1 ] +-------------------+ [ observer 2 ] | | [ observer n ] | + __call__() | | | +-------------------+ | | / \ \ / +-------------------+ | Subject | +-------------------+ | | | + attach(observer)| | + get() | | + set() | | | +-------------------+
L’esempio seguente (tratto dal testo Python 3 Object Oriented Programming),
è piuttosto semplice:
class WareHouse: def __init__(self): self.observers = [] self.product = None self.quantity = 0 def attach(self, observer): self.observers.append(observer) def get_product(self): return self.product def set_product(self, value): self.product = value self.update_observers() def get_quantity(self): return self.quantity def set_quantity(self, value): self.quantity = value self.update_observers() def update_observers(self): for observer in self.observers: observer() class Observer: def __init__(self, warehouse): self.warehouse = warehouse def __call__(self): print 'product: {}'.format(self.warehouse.product) print 'qty left: {}'.format(self.warehouse.quantity)
il funzionamento è il seguente:
>>> wh = WareHouse()
creaiamo un’istanza di WareHouse che, durante l’inizializzazione,
inizializza i valori delle variabili.
self.observers è una lista che conterrà tutte le istanze di Observer che creeremo.
>>> o = Observer(wh)
creiamo la prima istanza di Observer, che deve avere come parametro il soggetto
da ‘osservare’, cioè l’istanza di WareHouse ‘wh’.
>>> wh.attach(o)
fondamentale nel soggetto il metodo attach, che permette di inserire nella lista vuota,
l’observer appena creato.
>>> wh.set_product('keyboards') product: keyboards qty left: 0
Qui associamo alla variabile ‘product’ il valore ‘keyboards’, poichè in fase di inizializzazione
dell’istanza di WareHouse, product = None.
Chiamando il metodo set_product, oltre al settaggio della variabile, si ha una chiamata al
metodo update_observers(), il quale scorre la lista degli observers e li chiama ad uno ad uno.
Come avviene questo?
Grazie al metodo speciale __call__ di Observer, che lo rende callable (chiamabile)
for observer in self.observers: observer()
>>> wh.set_quantity(10) product: keyboards qty left: 10
come spiegato nel punto precedente.
Siccome siamo di coccio, ma vogliamo migliorare, possiamo rendere il codice più elegante usando
property al posto dei metodi get/set.
Condizione necessaria però, l’utilizzo di classi new-style (cioè che WhareHouse e Observer
derivino esplicitamente da object):
class WareHouse(object): def __init__(self): self.observers = [] self._product = None # utilizzo _product per non entrare in conflitto self._quantity = 0 # con i nomi dei metodi def attach(self, observer): self.observers.append(observer) @property def product(self): return self._product @product.setter def product(self, value): self._product = value self._update_observers() @property def quantity(self): return self._quantity @quantity.setter def quantity(self, value): self._quantity = value self._update_observers() def _update_observers(self): for observer in self.observers: observer()
esempio di funzionamento:
>>> w = WareHouse() >>> o = Observer(w) >>> w.attach(o) >>> w.product = 'joypads' product: joypads qty left: 0 >>> w.quantity = 10 product: joypads qty left: 10 >>> w.quantity = 9 product: joypads qty left: 9 >>> o2 = Observer(w) >>> w.attach(o2) >>> w.observers [<__main__.Observer object at 0x011B3F10>, <__main__.Observer object at 0x011CCEF0>] >>> w.quantity = 8 product: joypads # observer n.1 qty left: 8 product: joypads # observer n.2 qty left: 8
La cosa bella del pattern è che possiamo utilizzare anche più observer contemporaneamente,
ognuno con compiti differenti, scollegati tra loro.
La cosa importante è che tali observers lavorano completamente all’insaputa dell’oggetto monitorato.
al prossimo pattern…
Commenti recenti