Progetto

Generale

Profilo

Singleton Pattern

Il pattern singleton viene utilizzato quando si vuole creare una sola instanza di una classe.

Proviamo a pensare al caso di parsing di dati; essendo questa una operazione lunga e dispendiosa in termini di calcolo vogliamo poter riutilizzare i risultati della prima "esecuzione".

Ecco un esempio:

# Singleton class
# If instance is null creates an instance calling +new+ from object
class Singleton(object):
        instance = None

        def +new+(cls):
                if cls.instance == None:
                        cls.instance = super(Singleton, cls).+new+(cls)
                return cls.instance

a = Singleton()

print type(a)
print id(a)

b = Singleton()
print type(b)
print id(b)

Risultato esecuzione:

<class '+main+.Singleton'>

-1211002548

<class '+main+.Singleton'>

-1211002548

Flyweight Pattern

Il pattern flyweight viene utilizzato quando si ha a che fare con un gran numero di oggetti con alcune caratteristiche comuni (che variano raramente) ed altre non comuni di cui ogni oggetto si fa carico per se.

Questo pattern può essere molto utile quando si vuole alleggerire il carico in ram di un grande numero di oggetti, facendo in modo di riutilizzare oggetti con "lo stesso stato".

Ecco un esempio di codice:

# Flyweight pattern
# La classe "TipoOggetto" mantiene informazione delle caratteristiche intrinseche degli oggetti (in comune)
class [[TipoOggetto]](object):
        _listaOggetti = {}

        def +new+(cls, name):
                oggetto = [[TipoOggetto]]._listaOggetti.get(name,None)

                if not oggetto:
                        oggetto = object.+new+(cls)
                        [[TipoOggetto]]._listaOggetti[name] = oggetto
                return oggetto

        def +init+(self, name):
                self.name = name
                print id(self), name

# Per mostrare come un oggetto può avere delle caratteristiche estrinseche si deve creare
# un'altra classe che utilizzi due oggetti di cui uno di tipo "TipoOggetto" 

class [[ProvaFlyweight]]:

        def +init+(self, name, altro):
                self.name = [[TipoOggetto]](name)
                self.altro = altro

a = [[ProvaFlyweight]]("obj1", "1")
b = [[ProvaFlyweight]]("obj2", "2")
c = [[ProvaFlyweight]]("obj3", "3")
d = [[ProvaFlyweight]]("obj1", "4")

print id(a.name)
print id(b.name)
print id(c.name)
print id(d.name)

print id(a.altro)
print id(b.altro)
print id(c.altro)
print id(d.altro)


-1210465204 obj1

-1210465140 obj2

-1210465076 obj3

-1210465204 obj1

-1210465204

-1210465140

-1210465076

-1210465204

-1210102464

-1210465600

-1210465536

-1210465440

Come si può vedere dal risultato dell'esecuzione l'attributo "name" dell'instanza "a" e "d" sono un unico oggetto.

Observer Pattern

Il pattern Observer definisce una dipendenza "uno a molti" tra oggetti. In questo pattern gli oggetti rivestono due ruoli:

  • Subject (il soggetto)
  • Listeners (gli oggetti che ascoltano il soggetto)

I listeners (o observer) si mettono in ascolto del soggetto; quando il soggetto "cambia stato" allora aggiorna tutti i listener in ascolto (registrati) su di lui. Questo paradigma di programmazione è quello che in alcuni linguaggi si trova nativo, ed è chiamato programmazione ad eventi.

Ecco un esempio:

class Subject:
        +listeners = []

        def register(self, listener):
                self.+listeners.append(listener)

        def +notifyAll(self):
                for entry in self.+listeners:
                        entry.notify()

        def statusChanged(self):
                self.+notifyAll()

class Listener:

        def +init+(self, listenerName):
                self.name = listenerName

        def notify(self):
                print "Notify on listener ", self.name

s = Subject()

l1 = Listener("1")
l2 = Listener("2")
l3 = Listener("3")
l4 = Listener("4")
l5 = Listener("5")

s.register(l1)
s.register(l2)
s.register(l3)
s.register(l4)
s.register(l5)

s.statusChanged()

Decorator Pattern

E' un pattern abbastanza semplice che è parte fondamentale dell'OOP. Serve per aggiungere funzionalità agli oggetti.

Vediamo un esempio:

class A:
        def +init+(self, b):
            """ b è una istanza della classe B""" 
            self.b = b

        def +getattr+(self, name):
            """ I metodi/attributi di A non devono conoscere come sono delegati a b """ 
            return getattr(self.b, name)

b = B()
a = A(b)

Il metodo getaddr viene chiamato quando non vengono trovati gli attributi, quindi in automatico si vanno a ricercare nella
classe B, senza dovere distinguere le cose.