Szukaj…


Wprowadzenie

Wzorzec projektowy jest ogólnym rozwiązaniem często występującego problemu w tworzeniu oprogramowania. Ten temat dokumentacji ma na celu dostarczenie przykładów typowych wzorców projektowych w Pythonie.

Wzór strategii

Ten wzorzec projektu nazywa się Wzorzec strategii. Służy do definiowania rodziny algorytmów, enkapsulacji każdego z nich i umożliwienia ich zamiany. Wzór strategii pozwala algorytmowi zmieniać się niezależnie od klientów, którzy go używają.

Na przykład zwierzęta mogą „chodzić” na wiele różnych sposobów. Chodzenie można uznać za strategię wdrażaną przez różne typy zwierząt:

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

Uruchomienie tego przykładu spowoduje wygenerowanie następującego wyniku:

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 

Zauważ, że w językach takich jak C ++ lub Java ten wzorzec jest implementowany przy użyciu klasy abstrakcyjnej lub interfejsu w celu zdefiniowania strategii. W Pythonie bardziej sensowne jest po prostu zdefiniowanie zewnętrznych funkcji, które można dynamicznie dodawać do klasy za pomocą types.MethodType .

Wprowadzenie do wzorców projektowych i wzorca Singleton

Wzorce projektowe zapewniają rozwiązania commonly occurring problems w projektowaniu oprogramowania. Wzory projektowe zostały po raz pierwszy wprowadzone przez GoF(Gang of Four) których opisali wspólne wzorce jako problemy, które pojawiają się w kółko i rozwiązania tych problemów.

Wzory projektowe mają cztery podstawowe elementy:

  1. The pattern name to uchwyt, którego możemy użyć do opisania problemu projektowego, jego rozwiązań i konsekwencji w jednym lub dwóch słowach.
  2. The problem opisuje, kiedy zastosować wzór.
  3. The solution opisuje elementy, które składają się na projekt, ich relacje, obowiązki i współpracę.
  4. The consequences są wyniki i kompromisy związane z zastosowaniem wzorca.

Zalety wzorów projektowych:

  1. Można je wykorzystywać wielokrotnie w wielu projektach.
  2. Architektoniczny poziom problemów można rozwiązać
  3. Są sprawdzone i sprawdzone w czasie, co stanowi doświadczenie deweloperów i architektów
  4. Mają niezawodność i zależność

Wzory projektowe można podzielić na trzy kategorie:

  1. Wzór kreacyjny
  2. Wzór strukturalny
  3. Wzór behawioralny

Creational Pattern - zajmują się sposobem tworzenia obiektu i izolują szczegóły tworzenia obiektu.

Structural Pattern - projektują strukturę klas i obiektów, aby mogli komponować w celu uzyskania większych wyników.

Behavioral Pattern - dotyczą interakcji między przedmiotami i odpowiedzialności za przedmioty.

Wzór singletonu :

Jest to rodzaj creational pattern który zapewnia mechanizm posiadania tylko jednego i jednego obiektu danego typu i zapewnia globalny punkt dostępu.

np. Singleton może być wykorzystywany w operacjach bazy danych, w których chcemy, aby obiekt bazy danych zachował spójność danych.

Realizacja

Możemy zaimplementować wzorzec Singleton w Pythonie, tworząc tylko jedną instancję klasy Singleton i ponownie obsługując ten sam obiekt.

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)

Wynik:

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

Zauważ, że w językach takich jak C ++ lub Java ten wzorzec jest implementowany poprzez uczynienie konstruktora prywatnym i utworzenie statycznej metody inicjującej obiekt. W ten sposób jeden obiekt jest tworzony przy pierwszym wywołaniu, a klasa zwraca ten sam obiekt później. Ale w Pythonie nie mamy możliwości tworzenia prywatnych konstruktorów.

Wzór fabryczny

Wzorzec fabryczny jest również Creational pattern . Termin factory oznacza, że klasa jest odpowiedzialna za tworzenie obiektów innych typów. Istnieje klasa, która działa jak fabryka, z którą są powiązane obiekty i metody. Klient tworzy obiekt, wywołując metody o określonych parametrach, a fabryka tworzy obiekt pożądanego typu i zwraca go do klienta.

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)

Wynik:

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

MusicFactory to klasa fabryczna, która tworzy obiekt typu Mp3 lub Ogg zależności od wyboru użytkownika.

Pełnomocnik

Obiekt proxy jest często używany do zapewnienia strzeżonego dostępu do innego obiektu, którego wewnętrznej logiki biznesowej nie chcemy zanieczyszczać wymogami bezpieczeństwa.

Załóżmy, że chcielibyśmy zagwarantować, że tylko użytkownik określonych uprawnień może uzyskać dostęp do zasobów.

Definicja serwera proxy: (zapewnia, że tylko użytkownicy, którzy faktycznie widzą rezerwacje, będą mogli korzystać z usługi booking_service)

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)

KORZYŚCI
  • unikamy jakichkolwiek zmian w ReservationService przy zmianie ograniczeń dostępu.
  • my nie mieszanie danych biznesowych powiązanych ( date_from , date_to , reservations_count ) z domeny niepowiązanych pojęć (uprawnienia użytkownika) w służbie.
  • Konsument ( StatsService ) jest również wolny od logiki związanej z uprawnieniami

PRZESTROGI
  • Interfejs proxy jest zawsze dokładnie taki sam jak obiekt, który ukrywa, więc użytkownik korzystający z usługi opakowanej przez proxy nie był nawet świadomy obecności proxy.


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow