Python Language
Designmuster
Suche…
Einführung
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:
-
The pattern name
ist ein Handle, mit dem wir ein Designproblem, seine Lösungen und Konsequenzen in ein oder zwei Worten beschreiben können. -
The problem
beschreibt, wann das Muster angewendet wird. -
The solution
beschreibt die Elemente, aus denen das Design besteht, sowie deren Beziehungen, Verantwortlichkeiten und Kollaborationen. -
The consequences
sind die Ergebnisse und Kompromisse bei der Anwendung des Musters.
Vorteile von Design Patterns:
- Sie können in mehreren Projekten wiederverwendet werden.
- Die architektonische Ebene der Probleme kann gelöst werden
- Sie sind langjährig erprobt und bewährt. Dies ist die Erfahrung von Entwicklern und Architekten
- Sie haben Zuverlässigkeit und Abhängigkeit
Entwurfsmuster können in drei Kategorien unterteilt werden:
- Kreationelles Muster
- Strukturelles Muster
- 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
) mitdate_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.