Zoeken…


Invoering

Een ontwerppatroon is een algemene oplossing voor een veel voorkomend probleem bij softwareontwikkeling. Dit documentatieonderwerp is specifiek bedoeld om voorbeelden te geven van algemene ontwerppatronen in Python.

Strategie patroon

Dit ontwerppatroon wordt Strategiepatroon genoemd. Het wordt gebruikt om een familie van algoritmen te definiëren, deze in te kapselen en ze uitwisselbaar te maken. Door het ontwerppatroon van de strategie kan een algoritme onafhankelijk variëren van klanten die het gebruiken.

Dieren kunnen bijvoorbeeld op veel verschillende manieren "lopen". Lopen kan worden beschouwd als een strategie die wordt uitgevoerd door verschillende soorten dieren:

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

Het uitvoeren van dit voorbeeld zou de volgende uitvoer produceren:

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 

Merk op dat in talen zoals C ++ of Java dit patroon wordt geïmplementeerd met behulp van een abstracte klasse of een interface om een strategie te definiëren. In Python is het types.MethodType om enkele functies extern te definiëren die dynamisch aan een klasse kunnen worden toegevoegd met behulp van types.MethodType .

Inleiding tot ontwerppatronen en Singleton-patroon

Ontwerppatronen bieden oplossingen voor de commonly occurring problems bij het ontwerpen van software. De ontwerppatronen werden voor het eerst geïntroduceerd door GoF(Gang of Four) waar ze de gemeenschappelijke patronen beschreven als problemen die zich steeds opnieuw voordoen en oplossingen voor die problemen.

Ontwerppatronen hebben vier essentiële elementen:

  1. The pattern name is een handvat dat we kunnen gebruiken om een ontwerpprobleem, de oplossingen en de gevolgen ervan in een paar woorden te beschrijven.
  2. The problem beschrijft wanneer het patroon moet worden toegepast.
  3. The solution beschrijft de elementen waaruit het ontwerp bestaat, hun relaties, verantwoordelijkheden en samenwerkingen.
  4. The consequences zijn de resultaten en afwegingen van het toepassen van het patroon.

Voordelen van ontwerppatronen:

  1. Ze zijn herbruikbaar voor meerdere projecten.
  2. Het architectonische niveau van problemen kan worden opgelost
  3. Ze zijn beproefd en beproefd, wat de ervaring is van ontwikkelaars en architecten
  4. Ze hebben betrouwbaarheid en afhankelijkheid

Ontwerppatronen kunnen in drie categorieën worden ingedeeld:

  1. Creatief patroon
  2. Structureel patroon
  3. Gedragspatroon

Creational Pattern - Ze houden zich bezig met hoe het object kan worden gemaakt en isoleren de details van het maken van objecten.

Structural Pattern - Ze ontwerpen de structuur van klassen en objecten zodat ze kunnen componeren om grotere resultaten te bereiken.

Behavioral Pattern - Ze houden zich bezig met interactie tussen objecten en de verantwoordelijkheid van objecten.

Singleton-patroon :

Het is een soort creational pattern dat een mechanisme biedt om slechts één en één object van een bepaald type te hebben en een globaal toegangspunt biedt.

bijvoorbeeld Singleton kan worden gebruikt bij databasebewerkingen, waarbij we willen dat databaseobjecten de gegevensconsistentie behouden.

Implementatie

We kunnen Singleton Pattern in Python implementeren door slechts één instantie van de Singleton-klasse te maken en hetzelfde object opnieuw te dienen.

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)

Output:

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

Merk op dat in talen zoals C ++ of Java dit patroon wordt geïmplementeerd door de constructor privé te maken en een statische methode te maken die de objectinitialisatie uitvoert. Op deze manier wordt één object gemaakt bij de eerste aanroep en retourneert de klasse daarna hetzelfde object. Maar in Python hebben we geen enkele manier om privéconstructeurs te maken.

Fabriek patroon

Fabriekspatroon is ook een Creational pattern . De term factory betekent dat een klasse verantwoordelijk is voor het maken van objecten van andere typen. Er is een klasse die fungeert als een fabriek waaraan objecten en methoden zijn gekoppeld. De client maakt een object door de methoden met bepaalde parameters aan te roepen en in de fabriek wordt het object van het gewenste type gemaakt en aan de client geretourneerd.

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)

Output:

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

MusicFactory is hier de fabrieksklasse die een object van het type Mp3 of Ogg afhankelijk van de keuze die de gebruiker biedt.

volmacht

Proxy-object wordt vaak gebruikt om beveiligde toegang tot een ander object te garanderen, welke interne bedrijfslogica we niet willen vervuilen met veiligheidseisen.

Stel dat we willen garanderen dat alleen gebruikers met specifieke machtigingen toegang hebben tot bronnen.

Proxy-definitie: (het zorgt ervoor dat alleen gebruikers die daadwerkelijk reserveringen kunnen zien, consumer_service kunnen gebruiken)

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)

VOORDELEN
  • we vermijden wijzigingen in ReservationService wanneer toegangsbeperkingen worden gewijzigd.
  • we date_from geen bedrijfsgerelateerde gegevens ( date_from , date_to , reservations_count ) met domeingerelateerde concepten (gebruikersrechten) in de service.
  • Consumer ( StatsService ) is ook vrij van logica met betrekking tot machtigingen

WAARSCHUWINGEN
  • De proxy-interface is altijd exact hetzelfde als het object dat het verbergt, zodat de gebruiker die service verpakt door proxy gebruikt niet eens op de hoogte was van de aanwezigheid van proxy.


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow