Suche…


Einführung

Ein Entwurfsmuster ist eine allgemeine Lösung für ein häufig auftretendes Problem in der Softwareentwicklung. In diesem Dokumentationsthema werden Beispiele für gängige Entwurfsmuster in Python beschrieben.

Strategiemuster

Dieses Entwurfsmuster wird Strategiemuster genannt. Es wird verwendet, um eine Familie von Algorithmen zu definieren, jeden zu kapseln und austauschbar zu machen. Das Strategieentwurfsmuster lässt einen Algorithmus unabhängig von den Kunden variieren, die ihn verwenden.

Zum Beispiel können Tiere auf viele verschiedene Arten "laufen". Gehen kann als eine Strategie betrachtet werden, die von verschiedenen Tierarten umgesetzt wird:

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

Das Ausführen dieses Beispiels würde die folgende Ausgabe erzeugen:

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 

Beachten Sie, dass in Sprachen wie C ++ oder Java dieses Muster mithilfe einer abstrakten Klasse oder einer Schnittstelle implementiert wird, um eine Strategie zu definieren. In Python ist es sinnvoller, nur einige Funktionen extern zu definieren, die mithilfe von types.MethodType dynamisch zu einer Klasse types.MethodType .

Einführung in Design Patterns und Singleton Pattern

Design Patterns bieten Lösungen für commonly occurring problems beim Softwaredesign. Die Entwurfsmuster wurden zuerst von GoF(Gang of Four) in dem sie die häufig vorkommenden Muster als Probleme beschrieben, die immer wieder auftreten, und Lösungen für diese Probleme.

Designmuster haben vier wesentliche Elemente:

  1. The pattern name ist ein Handle, mit dem wir ein Designproblem, seine Lösungen und Konsequenzen in ein oder zwei Worten beschreiben können.
  2. The problem beschreibt, wann das Muster angewendet wird.
  3. The solution beschreibt die Elemente, aus denen das Design besteht, sowie deren Beziehungen, Verantwortlichkeiten und Kollaborationen.
  4. The consequences sind die Ergebnisse und Kompromisse bei der Anwendung des Musters.

Vorteile von Design Patterns:

  1. Sie können in mehreren Projekten wiederverwendet werden.
  2. Die architektonische Ebene der Probleme kann gelöst werden
  3. Sie sind langjährig erprobt und bewährt. Dies ist die Erfahrung von Entwicklern und Architekten
  4. Sie haben Zuverlässigkeit und Abhängigkeit

Entwurfsmuster können in drei Kategorien unterteilt werden:

  1. Kreationelles Muster
  2. Strukturelles Muster
  3. Verhaltensmuster

Creational Pattern - Sie befassen sich mit der Erstellung des Objekts und isolieren die Details der Objekterstellung.

Structural Pattern - Sie gestalten die Struktur von Klassen und Objekten so, dass sie komponieren können, um größere Ergebnisse zu erzielen.

Behavioral Pattern - Sie befassen sich mit der Interaktion zwischen Objekten und der Verantwortung von Objekten.

Singleton-Muster :

Es handelt sich um eine Art creational pattern , das einen Mechanismus bereitstellt, um nur ein und ein Objekt eines bestimmten Typs zu haben und einen globalen Zugangspunkt bereitzustellen.

Zum Beispiel kann Singleton in Datenbankoperationen verwendet werden, bei denen Datenbankobjekte die Datenkonsistenz beibehalten sollen.

Implementierung

Wir können Singleton Pattern in Python implementieren, indem Sie nur eine Instanz der Singleton-Klasse erstellen und dasselbe Objekt erneut bedienen.

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)

Ausgabe:

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

Beachten Sie, dass dieses Muster in Sprachen wie C ++ oder Java implementiert wird, indem Sie den Konstruktor privat machen und eine statische Methode erstellen, die die Objektinitialisierung durchführt. Auf diese Weise wird beim ersten Aufruf ein Objekt erstellt, und die Klasse gibt danach dasselbe Objekt zurück. In Python haben wir jedoch keine Möglichkeit, private Konstruktoren zu erstellen.

Fabrikmuster

Das Fabrikmuster ist auch ein Creational pattern . Der Begriff factory bedeutet, dass eine Klasse für das Erstellen von Objekten anderer Typen verantwortlich ist. Es gibt eine Klasse, die als Factory fungiert und mit Objekten und Methoden verknüpft ist. Der Client erstellt ein Objekt durch Aufrufen der Methoden mit bestimmten Parametern. Factory erstellt das Objekt des gewünschten Typs und gibt es an den Client zurück.

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)

Ausgabe:

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

MusicFactory ist hier die Factory-Klasse, die je nach Wahl des Benutzers entweder ein Objekt vom Typ Mp3 oder Ogg .

Proxy

Proxy-Objekte werden häufig verwendet, um den geschützten Zugriff auf ein anderes Objekt sicherzustellen. Diese interne Geschäftslogik möchten wir nicht mit Sicherheitsanforderungen belasten.

Angenommen, wir möchten garantieren, dass nur Benutzer bestimmter Berechtigungen auf die Ressource zugreifen können.

Proxy-Definition: (Es wird sichergestellt, dass nur Benutzer, die tatsächlich Reservierungen sehen können, denesen reservation_service verwenden können.)

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)

LEISTUNGEN
  • wir vermeiden Änderungen in ReservationService wenn Zugriffsbeschränkungen geändert werden
  • Wir mischen keine geschäftsbezogenen Daten ( date_from , date_to , reservations_count ) mit date_to Konzepten (Benutzerberechtigungen) im Service.
  • Consumer ( StatsService ) ist ebenfalls frei von Logik für Berechtigungen

CAVEATS
  • Die Proxy-Schnittstelle entspricht immer genau dem Objekt, das sie verbirgt, so dass der Benutzer, der den von Proxy umschlossenen Dienst in Anspruch nimmt, nicht einmal die Anwesenheit eines Proxy erkannt hat.


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow