Ricerca…


introduzione

Un modello di progettazione è una soluzione generale a un problema comune nello sviluppo del software. Questo argomento di documentazione è specificamente volto a fornire esempi di modelli di progettazione comuni in Python.

Modello di strategia

Questo modello di design si chiama Strategy Pattern. È usato per definire una famiglia di algoritmi, incapsula ognuno e li rende intercambiabili. Il modello di progettazione strategica consente ad un algoritmo di variare in modo indipendente dai client che lo utilizzano.

Ad esempio, gli animali possono "camminare" in molti modi diversi. Camminare potrebbe essere considerato una strategia implementata da diversi tipi di animali:

from types import MethodType


class Animal(object):
    
    def __init__(self, *args, **kwargs):
        self.name = kwargs.pop('name', None) or 'Animal'
        if kwargs.get('walk', None):
            self.walk = MethodType(kwargs.pop('walk'), self)

    def walk(self):
        """
        Cause animal instance to walk
        
        Walking funcionallity is a strategy, and is intended to
        be implemented separately by different types of animals.
        """
        message = '{} should implement a walk method'.format(
            self.__class__.__name__)
        raise NotImplementedError(message)


# Here are some different walking algorithms that can be used with Animal
def snake_walk(self):
    print('I am slithering side to side because I am a {}.'.format(self.name))

def four_legged_animal_walk(self):
    print('I am using all four of my legs to walk because I am a(n) {}.'.format(
        self.name))

def two_legged_animal_walk(self):
    print('I am standing up on my two legs to walk because I am a {}.'.format(
        self.name))

L'esecuzione di questo esempio produrrebbe il seguente risultato:

generic_animal = Animal()
king_cobra = Animal(name='King Cobra', walk=snake_walk)
elephant = Animal(name='Elephant', walk=four_legged_animal_walk)
kangaroo = Animal(name='Kangaroo', walk=two_legged_animal_walk)

kangaroo.walk()
elephant.walk()
king_cobra.walk()
# This one will Raise a NotImplementedError to let the programmer
# know that the walk method is intended to be used as a strategy.
generic_animal.walk()

    # OUTPUT:
    #
    # I am standing up on my two legs to walk because I am a Kangaroo.
    # I am using all four of my legs to walk because I am a(n) Elephant.
    # I am slithering side to side because I am a King Cobra.
    # Traceback (most recent call last):
    #   File "./strategy.py", line 56, in <module>
    #     generic_animal.walk()
    #   File "./strategy.py", line 30, in walk
    #     raise NotImplementedError(message)
    # NotImplementedError: Animal should implement a walk method 

Nota che in linguaggi come C ++ o Java, questo modello è implementato usando una classe astratta o un'interfaccia per definire una strategia. In Python ha più senso definire solo esternamente alcune funzioni che possono essere aggiunte dinamicamente ad una classe usando types.MethodType .

Introduzione ai modelli di design e Singleton Pattern

I modelli di progettazione forniscono soluzioni ai commonly occurring problems nella progettazione del software. I modelli di progettazione sono stati introdotti per la prima volta da GoF(Gang of Four) dove hanno descritto gli schemi comuni come problemi che si verificano ripetutamente e soluzioni a tali problemi.

I modelli di progettazione hanno quattro elementi essenziali:

  1. The pattern name è una maniglia che possiamo usare per descrivere un problema di progettazione, le sue soluzioni e le conseguenze in una parola o due.
  2. The problem descrive quando applicare il modello.
  3. The solution descrive gli elementi che compongono il design, le loro relazioni, responsabilità e collaborazioni.
  4. The consequences sono i risultati e i trade-off dell'applicazione del modello.

Vantaggi dei modelli di progettazione:

  1. Sono riutilizzabili in più progetti.
  2. Il livello architettonico dei problemi può essere risolto
  3. Sono testati nel tempo e ben collaudati, che è l'esperienza di sviluppatori e architetti
  4. Hanno affidabilità e dipendenza

Gli schemi di progettazione possono essere classificati in tre categorie:

  1. Modello creativo
  2. Modello strutturale
  3. Modello comportamentale

Creational Pattern creativo: si preoccupano di come l'oggetto può essere creato e isolano i dettagli della creazione dell'oggetto.

Structural Pattern : progettano la struttura di classi e oggetti in modo che possano comporsi per ottenere risultati più ampi.

Behavioral Pattern - Sono interessati all'interazione tra oggetti e alla responsabilità degli oggetti.

Singleton Pattern :

È un tipo di creational pattern che fornisce un meccanismo per avere solo uno e un oggetto di un determinato tipo e fornisce un punto di accesso globale.

Ad esempio Singleton può essere utilizzato nelle operazioni di database, dove vogliamo che l'oggetto del database mantenga la coerenza dei dati.

Implementazione

Possiamo implementare Singleton Pattern in Python creando solo un'istanza della classe Singleton e servendo di nuovo lo stesso oggetto.

class Singleton(object):
    def __new__(cls):
        # hasattr method checks if the class object an instance property or not.
        if not hasattr(cls, 'instance'):
            cls.instance = super(Singleton, cls).__new__(cls)
        return cls.instance

s = Singleton()
print ("Object created", s)

s1 = Singleton()
print ("Object2 created", s1)

Produzione:

('Object created', <__main__.Singleton object at 0x10a7cc310>)
('Object2 created', <__main__.Singleton object at 0x10a7cc310>)

Si noti che in linguaggi come C ++ o Java, questo modello viene implementato rendendo privato il costruttore e creando un metodo statico che esegue l'inizializzazione dell'oggetto. In questo modo, un oggetto viene creato alla prima chiamata e la classe restituisce lo stesso oggetto in seguito. Ma in Python, non abbiamo alcun modo per creare costruttori privati.

Modello di fabbrica

Il modello di fabbrica è anche un Creational pattern . Il termine factory indica che una classe è responsabile della creazione di oggetti di altri tipi. Esiste una classe che agisce come una fabbrica che ha oggetti e metodi ad essa associati. Il client crea un oggetto chiamando i metodi con determinati parametri e factory crea l'oggetto del tipo desiderato e lo restituisce al client.

from abc import ABCMeta, abstractmethod

class Music():
    __metaclass__ = ABCMeta
    @abstractmethod
    def do_play(self):
        pass

class Mp3(Music):
    def do_play(self):
        print ("Playing .mp3 music!")
    
class Ogg(Music):
    def do_play(self):
        print ("Playing .ogg music!")
    
class MusicFactory(object):
    def play_sound(self, object_type):
        return eval(object_type)().do_play()
    
if __name__ == "__main__":
    mf = MusicFactory()
    music = input("Which music you want to play Mp3 or Ogg")
    mf.play_sound(music)

Produzione:

Which music you want to play Mp3 or Ogg"Ogg"
Playing .ogg music!

MusicFactory è la classe di fabbrica qui che crea un oggetto di tipo Mp3 o Ogg seconda della scelta dell'utente.

delega

L'oggetto proxy viene spesso utilizzato per garantire l'accesso protetto a un altro oggetto, che la logica aziendale interna non vogliamo inquinare con i requisiti di sicurezza.

Supponiamo di voler garantire che solo l'utente con autorizzazioni specifiche possa accedere alla risorsa.

Definizione del proxy: (garantisce che solo gli utenti che effettivamente possono vedere le prenotazioni saranno in grado di prenotare il servizio di prenotazione)

from datetime import date
from operator import attrgetter

class Proxy:
    def __init__(self, current_user, reservation_service):
        self.current_user = current_user
        self.reservation_service = reservation_service

    def highest_total_price_reservations(self, date_from, date_to, reservations_count):
        if self.current_user.can_see_reservations:
            return self.reservation_service.highest_total_price_reservations(
                date_from,
                date_to,
                reservations_count
              )
        else:
            return []

#Models and ReservationService:

class Reservation:
    def __init__(self, date, total_price):
        self.date = date
        self.total_price = total_price

class ReservationService:
    def highest_total_price_reservations(self, date_from, date_to, reservations_count):
        # normally it would be read from database/external service
        reservations = [
            Reservation(date(2014, 5, 15), 100),
            Reservation(date(2017, 5, 15), 10),
            Reservation(date(2017, 1, 15), 50)
        ]

        filtered_reservations = [r for r in reservations if (date_from <= r.date <= date_to)]

        sorted_reservations = sorted(filtered_reservations, key=attrgetter('total_price'), reverse=True)

        return sorted_reservations[0:reservations_count]


class User:
    def __init__(self, can_see_reservations, name):
        self.can_see_reservations = can_see_reservations
        self.name = name

#Consumer service:

class StatsService:
    def __init__(self, reservation_service):
        self.reservation_service = reservation_service

    def year_top_100_reservations_average_total_price(self, year):
        reservations = self.reservation_service.highest_total_price_reservations(
            date(year, 1, 1),
            date(year, 12, 31),
            1
        )

        if len(reservations) > 0:
            total = sum(r.total_price for r in reservations)

            return total / len(reservations)
        else:
            return 0

#Test:
def test(user, year):
    reservations_service = Proxy(user, ReservationService())
    stats_service = StatsService(reservations_service)
    average_price = stats_service.year_top_100_reservations_average_total_price(year)
    print("{0} will see: {1}".format(user.name, average_price))

test(User(True, "John the Admin"), 2017)
test(User(False, "Guest"),         2017)

BENEFICI
  • stiamo evitando qualsiasi modifica in ReservationService quando vengono modificate le restrizioni di accesso.
  • non stiamo mescolando i dati relativi al business ( date_from , date_to , reservations_count ) con concetti non collegati al dominio (permessi dell'utente) in servizio.
  • Anche Consumer ( StatsService ) è libero dalla logica relativa alle autorizzazioni

CAVEATS
  • L'interfaccia proxy è sempre esattamente uguale all'oggetto che nasconde, quindi l'utente che utilizza il servizio fornito dal proxy non era nemmeno a conoscenza della presenza del proxy.


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow