Sök…


Introduktion

Ett designmönster är en allmän lösning på ett vanligt förekommande problem inom mjukvaruutveckling. Detta dokumentationsämne syftar specifikt till att ge exempel på vanliga designmönster i Python.

Strategimönster

Detta designmönster kallas Strategimönster. Det används för att definiera en familj av algoritmer, kapslar in var och en och gör dem utbytbara. Strategi designmönster låter en algoritm variera oberoende från klienter som använder den.

Djur kan till exempel "gå" på många olika sätt. Walking kan betraktas som en strategi som implementeras av olika typer av djur:

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

Att köra detta exempel skulle producera följande utgång:

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 

Observera att på språk som C ++ eller Java implementeras detta mönster med en abstrakt klass eller ett gränssnitt för att definiera en strategi. I Python är det mer meningsfullt att bara definiera vissa funktioner externt som kan läggas till dynamiskt i en klass med hjälp av types.MethodType .

Introduktion till designmönster och Singletonmönster

Designmönster ger lösningar på de commonly occurring problems i programvarudesign. Designmönstren introducerades först av GoF(Gang of Four) där de beskrev de vanliga mönstren som problem som uppstår om och om igen och lösningar på dessa problem.

Designmönster har fyra väsentliga element:

  1. The pattern name är ett handtag som vi kan använda för att beskriva ett designproblem, dess lösningar och konsekvenser i ett eller två ord.
  2. The problem beskriver när mönstret ska tillämpas.
  3. The solution beskriver de element som utgör designen, deras relationer, ansvar och samarbeten.
  4. The consequences är resultaten och avvägningarna av att tillämpa mönstret.

Fördelar med designmönster:

  1. De kan återanvändas i flera projekt.
  2. Den arkitektoniska nivån på problem kan lösas
  3. De är tidtestade och väl beprövade, vilket är erfarenheten från utvecklare och arkitekter
  4. De har tillförlitlighet och beroende

Designmönster kan klassificeras i tre kategorier:

  1. Skapningsmönster
  2. Strukturella mönster
  3. Beteendemönster

Creational Pattern - De handlar om hur objektet kan skapas och de isolerar detaljerna i objektskapandet.

Structural Pattern - De utformar strukturen för klasser och objekt så att de kan komponera för att uppnå större resultat.

Behavioral Pattern - De är upptagna med interaktion mellan objekt och objekt.

Singletonmönster :

Det är en typ av creational pattern som tillhandahåller en mekanism som endast har ett och ett objekt av en viss typ och ger en global tillgångspunkt.

t.ex. Singleton kan användas i databasoperationer, där vi vill att databasobjekt ska upprätthålla datakonsistensen.

Genomförande

Vi kan implementera Singleton Pattern i Python genom att bara skapa en instans av Singleton-klassen och servera samma objekt igen.

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)

Produktion:

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

Observera att på språk som C ++ eller Java implementeras detta mönster genom att göra konstruktören privat och skapa en statisk metod som gör objektinitieringen. På så sätt skapas ett objekt vid det första samtalet och klassen returnerar samma objekt därefter. Men i Python har vi inget sätt att skapa privata konstruktörer.

Fabriksmönster

Fabriksmönster är också ett Creational pattern . Termen factory betyder att en klass ansvarar för att skapa objekt av andra typer. Det finns en klass som fungerar som en fabrik som har föremål och metoder förknippade med den. Klienten skapar ett objekt genom att anropa metoderna med vissa parametrar och fabriken skapar objektet av önskad typ och returnerar det till klienten.

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)

Produktion:

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

MusicFactory är fabriksklassen här som skapar antingen ett objekt av typen Mp3 eller Ogg beroende på vilket val användaren ger.

Ombud

Proxy-objekt används ofta för att säkerställa skyddad åtkomst till ett annat objekt, vilken intern affärslogik vi inte vill förorena med säkerhetskraven.

Anta att vi vill garantera att endast användare med specifika behörigheter kan komma åt resursen.

Proxy-definition: (det säkerställer att endast användare som faktiskt kan se reservationer kan konsumentreservation_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)

FÖRDELAR
  • vi undviker alla ändringar i ReservationService när åtkomstbegränsningar ändras.
  • Vi blandar inte affärsrelaterade data ( date_from , date_to , reservations_count ) med domänrelaterade koncept (användarrättigheter) i tjänsten.
  • Konsument ( StatsService ) är också fri från behörighetsrelaterad logik

TÄNK
  • Proxy-gränssnittet är alltid exakt samma som objektet som det döljer, så att användare som konsumerar tjänster som är inslagna av proxy inte ens var medvetna om proxy-närvaro.


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow