Factory-Muster
Teil des Abschnitts Object Oriented Programming der Python-Journey von Coddy — Lektion 46 von 64.
Das Factory-Pattern erzeugt Objekte, ohne deren genaue Klasse anzugeben. Anstatt Konstruktoren direkt aufzurufen, verwenden Sie eine Factory-Methode, die entscheidet, welche Klasse instanziiert werden soll.
Hier sind einfache Produktklassen:
class Car:
def __init__(self, brand):
self.brand = brand
self.type = "Car"
def info(self):
return f"{self.type}: {self.brand}"
class Bike:
def __init__(self, brand):
self.brand = brand
self.type = "Bike"
def info(self):
return f"{self.type}: {self.brand}"Erstellen Sie eine Factory-Klasse, um diese Objekte zu erzeugen:
class VehicleFactory:
def create_vehicle(self, vehicle_type, brand):
if vehicle_type == "car":
return Car(brand)
elif vehicle_type == "bike":
return Bike(brand)
else:
raise ValueError(f"Unknown type: {vehicle_type}")Verwenden Sie die Factory, anstatt Konstruktoren direkt aufzurufen:
factory = VehicleFactory()
my_car = factory.create_vehicle("car", "Toyota")
my_bike = factory.create_vehicle("bike", "Honda")
print(my_car.info()) # Auto: Toyota
print(my_bike.info()) # Motorrad: HondaMachen Sie die Factory flexibler mit *args:
class FlexibleFactory:
def create_vehicle(self, vehicle_type, *args):
if vehicle_type == "car":
return Car(args[0]) # Nur Marke
elif vehicle_type == "truck":
return Truck(args[0], args[1]) # Marke und Kapazität
else:
raise ValueError(f"Unknown type: {vehicle_type}")
class Truck:
def __init__(self, brand, capacity):
self.brand = brand
self.capacity = capacity
self.type = "Truck"
def info(self):
return f"{self.type}: {self.brand} ({self.capacity}t)"Verwenden Sie die flexible Factory:
flexible = FlexibleFactory()
car = flexible.create_vehicle("car", "Ford")
truck = flexible.create_vehicle("truck", "Volvo", "20")
print(car.info()) # Auto: Ford
print(truck.info()) # LKW: Volvo (20t)Ausgabe:
Car: Toyota
Bike: Honda
Car: Ford
Truck: Volvo (20t)Kernpunkt: Das Factory Pattern ermöglicht es Ihnen, Objekte zu erstellen, ohne deren genaue Klasse zu kennen. Die Factory-Methode entscheidet basierend auf Parametern, welche Klasse instanziiert werden soll. Verwenden Sie *args, um Produkte mit unterschiedlichen Konstruktorparametern zu handhaben. Dies macht Ihren Code flexibler und einfacher um neue Produkttypen zu erweitern.
Aufgabe
MittelIn dieser Herausforderung implementieren Sie ein Shape-Factory-System unter Verwendung eines ordnungsgemäßen objektorientierten Designs mit Vererbung und Polymorphismus.
Vervollständigen Sie die Implementierung in den folgenden Dateien:
shape.py- Basisklasse Shapecircle.py- Circle-Implementierungrectangle.py- Rectangle-Implementierungtriangle.py- Triangle-Implementierungshapefactory.py- Factory-Klasse zum Erstellen von Formen
Jede Datei enthält detaillierte TODO-Kommentare, die Sie bei Ihrer Implementierung unterstützen. Befolgen Sie diese Kommentare sorgfältig, um sicherzustellen, dass Ihr Code alle Anforderungen erfüllt.
Spickzettel
Das Factory-Pattern (Fabrikmuster) erstellt Objekte, ohne deren genaue Klasse anzugeben. Anstatt Konstruktoren direkt aufzurufen, verwenden Sie eine Factory-Methode, die entscheidet, welche Klasse instanziiert werden soll.
Grundlegende Factory-Implementierung:
class VehicleFactory:
def create_vehicle(self, vehicle_type, brand):
if vehicle_type == "car":
return Car(brand)
elif vehicle_type == "bike":
return Bike(brand)
else:
raise ValueError(f"Unknown type: {vehicle_type}")
# Verwendung
factory = VehicleFactory()
my_car = factory.create_vehicle("car", "Toyota")
my_bike = factory.create_vehicle("bike", "Honda")Flexible Factory unter Verwendung von *args für verschiedene Konstruktorparameter:
class FlexibleFactory:
def create_vehicle(self, vehicle_type, *args):
if vehicle_type == "car":
return Car(args[0]) # Nur Marke
elif vehicle_type == "truck":
return Truck(args[0], args[1]) # Marke und Kapazität
else:
raise ValueError(f"Unknown type: {vehicle_type}")
# Verwendung
flexible = FlexibleFactory()
car = flexible.create_vehicle("car", "Ford")
truck = flexible.create_vehicle("truck", "Volvo", "20")Hauptvorteile: Das Factory-Pattern macht den Code flexibler und einfacher um neue Produkttypen erweiterbar, ohne den bestehenden Client-Code zu ändern.
Probier es selbst
from shapefactory import ShapeFactory
from shape import Shape
from circle import Circle
from rectangle import Rectangle
from triangle import Triangle
import sys
# Testfall-Executor
test_case = input()
factory = ShapeFactory()
if test_case == "circle_area":
circle = factory.create_shape("circle", 5)
print(f"{circle.area():.2f}")
elif test_case == "rectangle_perimeter":
rectangle = factory.create_shape("rectangle", 4, 6)
print(f"{rectangle.perimeter()}")
elif test_case == "triangle_perimeter":
triangle = factory.create_shape("triangle", 3, 4, 5)
print(f"{triangle.perimeter()}")
elif test_case == "invalid_shape":
try:
factory.create_shape("hexagon", 6)
print("No exception raised")
except ValueError as e:
print(str(e))
elif test_case == "case_insensitive":
circle = factory.create_shape("CiRcLe", 3)
print(f"{circle.area():.2f}")
elif test_case == "shape_inheritance":
shapes = [
factory.create_shape("circle", 2),
factory.create_shape("rectangle", 2, 3),
factory.create_shape("triangle", 3, 4, 5)
]
all_shapes = all(isinstance(shape, Shape) for shape in shapes)
print(all_shapes)
elif test_case == "zero_radius_circle":
circle = factory.create_shape("circle", 0)
print(f"{circle.area():.2f} {circle.perimeter():.2f}")
elif test_case == "negative_dimensions":
rectangle = factory.create_shape("rectangle", -2, -3)
print(f"{rectangle.area()}")
elif test_case == "large_values":
circle = factory.create_shape("circle", 1000000)
print(f"{circle.area():.2e}")
elif test_case == "polymorphism_test":
shapes = [
factory.create_shape("circle", 2),
factory.create_shape("rectangle", 3, 4),
factory.create_shape("triangle", 3, 4, 5)
]
area_sum = sum(shape.area() for shape in shapes)
perimeter_sum = sum(shape.perimeter() for shape in shapes)
print(f"Area sum: {area_sum:.2f}, Perimeter sum: {perimeter_sum:.2f}")
elif test_case == "triangle_area":
triangle = factory.create_shape("triangle", 3, 4, 5)
print(f"{triangle.area():.2f}")
elif test_case == "method_override":
circle = factory.create_shape("circle", 2)
rectangle = factory.create_shape("rectangle", 3, 4)
triangle = factory.create_shape("triangle", 3, 4, 5)
# Methoden-Objekte abrufen, um Implementierungen zu vergleichen
circle_area = Circle.area
rectangle_area = Rectangle.area
triangle_area = Triangle.area
circle_perimeter = Circle.perimeter
rectangle_perimeter = Rectangle.perimeter
triangle_perimeter = Triangle.perimeter
# Prüfen, ob alle Implementierungen eindeutig sind
unique_areas = len({circle_area, rectangle_area, triangle_area}) == 3
unique_perimeters = len({circle_perimeter, rectangle_perimeter, triangle_perimeter}) == 3
if unique_areas and unique_perimeters:
print("All shapes correctly override methods")
else:
print("Some shapes share method implementations")Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Object Oriented Programming
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDer self-ParameterMethodenAttributeKonstruktor-Methode (__init__)Zusammenfassung – Einfacher Taschenrechner4Vererbung
Grundlagen der VererbungDie super()-FunktionMethoden überschreibenMehrfachvererbungMethod Resolution OrderZusammenfassung - Mitarbeiter-Hierarchie7Spezielle Methoden
Einführung in Magic MethodsOperator-ÜberladungMagic Methods für ContainerRückblick - Eigene Liste10Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster13Abschlussherausforderungen
E-Learning-PlattformBanksystemSpielcharakter-EntwicklungFahrzeugvermietung2Dekoratoren
Einführung in DekoratorenProperty-DekoratorStatischer Methoden-DekoratorKlassenmethoden-Dekorator5Polymorphismus
Methoden-Überschreiben vertieftDuck TypingAbstrakte Klassen und MethodenInterface-DesignZusammenfassung – Formen-Rechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungMixinsStatische und KlassenmethodenKlassendekoratorenContext Manager3Klasseneigenschaften
Instanz- vs. KlassenvariablenProperty-DekoratorenPrivate AttributeZusammenfassung – Bankkonto-Manager6Kapselung
Public, Protected, Private MemberZugriffsmodifikatorenInformation HidingProperty-Decorators für FortgeschritteneZusammenfassung - Studentenverwaltungssystem12Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen