Python-Strategiemuster
Das Strategiemuster ist ein Verhaltens-Entwurfsmuster, das es Ihnen ermöglicht, zur Laufzeit einen Algorithmus oder ein Verhalten auszuwählen. Einfach gesagt, kapselt das Strategiemuster verschiedene Algorithmen in unabhängige Klassen, sodass sie sich gegenseitig ersetzen können.
Kernidee
Stellen Sie sich vor, Sie gehen in ein Restaurant, um Essen zu bestellen: Sie können verschiedene Zahlungsmethoden wählen – Bargeld, Kreditkarte, mobile Zahlung usw. Egal, welche Zahlungsmethode Sie wählen, Sie können die Zahlung letztendlich abschließen, aber der konkrete Zahlungsvorgang ist unterschiedlich. Dies ist ein reales Beispiel für das Strategiemuster.
Entwurfsprinzipien
Das Strategie-Muster folgt den folgenden wichtigen Prinzipien:
- Open-Closed-Prinzip: Offen für Erweiterung, geschlossen für Modifikation.
- Single-Responsibility-Prinzip: Jede Strategieklasse ist nur für einen Algorithmus verantwortlich.
- Dependency-Inversion-Prinzip: Von Abstraktionen abhängen, nicht von konkreten Implementierungen.
Struktur des Strategie-Musters
Lassen Sie uns die Zusammensetzung des Strategie-Musters anhand eines UML-Klassendiagramms verstehen:

Beschreibung der Komponenten
Kontext
- Hält eine Referenz auf ein Strategieobjekt.
- Kann Strategien dynamisch wechseln.
- Delegiert Arbeit an das Strategieobjekt.
Strategie (Strategie-Schnittstelle)
- Definiert die gemeinsame Schnittstelle für alle unterstützten Algorithmen.
- Deklariert die Methode zur Ausführung des Algorithmus.
ConcreteStrategy
- Implementiert die Strategie-Schnittstelle.
- Stellt eine konkrete Algorithmusimplementierung bereit.
Grundlegende Syntax und Implementierung
Definition der Strategie-Schnittstelle
In Python können wir eine abstrakte Basisklasse (ABC) verwenden, um die Strategie-Schnittstelle zu definieren:
Beispiel
class PaymentStrategy(ABC):
"""Abstrakte Basisklasse für Zahlungsstrategie"""
@abstractmethod
def pay(self, amount: float) -> bool:
"""Zahlungsmethode"""
pass
Konkrete Strategie-Implementierung
Beispiel
"""Kreditkarten-Zahlungsstrategie"""
def __init__(self, card_number: str, expiry_date: str, cvv: str):
self.card_number = card_number
self.expiry_date = expiry_date
self.cvv = cvv
def pay(self, amount: float) -> bool:
print(f"Zahle {amount} yuan mit Kreditkarte")
print(f"Kartennummer: {self.card_number}")
# Die tatsächliche Zahlungslogik sollte hier stehen
return True
class AlipayPayment(PaymentStrategy):
"""Alipay-Zahlungsstrategie"""
def __init__(self, account: str):
self.account = account
def pay(self, amount: float) -> bool:
print(f"Zahle {amount} yuan mit Alipay")
print(f"Alipay-Konto: {self.account}")
# Die tatsächliche Zahlungslogik sollte hier stehen
return True
class WechatPayment(PaymentStrategy):
"""WeChat-Pay-Strategie"""
def __init__(self, openid: str):
self.openid = openid
def pay(self, amount: float) -> bool:
print(f"Zahle {amount} yuan mit WeChat Pay")
print(f"WeChat-OpenID: {self.openid}")
# Die tatsächliche Zahlungslogik sollte hier stehen
return True
Implementierung der Kontextklasse
Beispiel
"""Zahlungskontextklasse"""
def __init__(self, strategy: PaymentStrategy = None):
self._strategy = strategy
def set_strategy(self, strategy: PaymentStrategy):
"""Zahlungsstrategie festlegen"""
self._strategy = strategy
def execute_payment(self, amount: float) -> bool:
"""Zahlung ausführen"""
if not self._strategy:
raise ValueError("Keine Zahlungsstrategie festgelegt")
return self._strategy.pay(amount)
Vollständiges Beispiel: E-Commerce-Zahlungssystem
Lassen Sie uns die praktische Anwendung des Strategiemusters anhand eines vollständigen E-Commerce-Zahlungssystems demonstrieren:
Beispiel
from typing import Dict, Any
# Strategie-Schnittstelle
class DiscountStrategy(ABC):
"""Rabattstrategie-Schnittstelle"""
@abstractmethod
def calculate_discount(self, original_price: float) -> float:
"""Preis nach Rabatt berechnen"""
pass
# Konkrete Strategieimplementierungen
class NoDiscountStrategy(DiscountStrategy):
"""Keine-Rabatt-Strategie"""
def calculate_discount(self, original_price: float) -> float:
return original_price
class PercentageDiscountStrategy(DiscountStrategy):
"""Prozentuale-Rabatt-Strategie"""
def __init__(self, percentage: float):
if not 0 <= percentage <= 100:
raise ValueError("Der Rabattprozentsatz muss zwischen 0 und 100 liegen")
self.percentage = percentage
def calculate_discount(self, original_price: float) -> float:
discount_amount = original_price * (self.percentage / 100)
return original_price - discount_amount
class FixedAmountDiscountStrategy(DiscountStrategy):
"""Strategie für festen Rabattbetrag"""
def __init__(self, discount_amount: float):
if discount_amount < 0:
raise ValueError("Der Rabattbetrag darf nicht negativ sein")
self.discount_amount = discount_amount
def calculate_discount(self, original_price: float) -> float:
return max(0, original_price - self.discount_amount)
class SeasonalDiscountStrategy(DiscountStrategy):
"""Saisonale Rabattstrategie"""
def __init__(self, base_discount: float, seasonal_multiplier: float):
self.base_discount = base_discount
self.seasonal_multiplier = seasonal_multiplier
def calculate_discount(self, original_price: float) -> float:
total_discount = self.base_discount * self.seasonal_multiplier
return max(0, original_price - total_discount)
# Kontextklasse
class ShoppingCart:
"""Warenkorbklasse"""
def __init__(self):
self.items = []
self._discount_strategy = NoDiscountStrategy()
def add_item(self, item: str, price: float):
"""Artikel hinzufügen"""
self.items.append({"item": item, "price": price})
def set_discount_strategy(self, strategy: DiscountStrategy):
"""Rabattstrategie festlegen"""
self._discount_strategy = strategy
def calculate_total(self) -> float:
"""Gesamtpreis berechnen"""
total = sum(item["price"] for item in self.items)
return self._discount_strategy.calculate_discount(total)
def display_cart(self):
"""Warenkorbinhalt anzeigen"""
print("Warenkorbinhalt:")
for item in self.items:
print(f" - {item['item']}: {item['price']} yuan")
original_total = sum(item["price"] for item in self.items)
final_total = self.calculate_total()
print(f"Originalpreis: {original_total} yuan")
print(f"Reduzierter Preis: {final_total} yuan")
if original_total != final_total:
discount = original_total - final_total
print(f"Ersparnis: {discount} yuan")
# Verwendungsbeispiel
def main():
# Warenkorb erstellen
cart = ShoppingCart()
# Artikel hinzufügen
cart.add_item("Python-Programmierbuch", 89.0)
cart.add_item("Funkmaus", 129.0)
cart.add_item("Mechanische Tastatur", 399.0)
print("=== Kein Rabatt ===")
cart.set_discount_strategy(NoDiscountStrategy())
cart.display_cart()
print("\n"=== 20 % Rabatt ===")
cart.set_discount_strategy(PercentageDiscountStrategy(20)) # 20 % Rabatt
cart.display_cart()
print("\n"=== Schwellenwertrabatt (50 Yuan Nachlass) ===")
cart.set_discount_strategy(FixedAmountDiscountStrategy(50))
cart.display_cart()
print("\n"=== Saisonrabatt ===")
cart.set_discount_strategy(SeasonalDiscountStrategy(30, 1.5)) # Basisrabatt 30, Saisonkoeffizient 1.5
cart.display_cart()
if __name__ == "__main__":
main()
Wenn Sie den obigen Code ausführen, sehen Sie die folgende Ausgabe:
=== 无折扣 === 购物车内容: - Python编程书: 89.0元 - 无线鼠标: 129.0元 - 机械键盘: 399.0元 原价: 617.0元 折后价: 617.0元 === 8折优惠 === 购物车内容: - Python编程书: 89.0元 - 无线鼠标: 129.0元 - 机械键盘: 399.0元 原价: 617.0元 折后价: 493.6元 节省: 123.4元 === 满减优惠(减50元)=== 购物车内容: - Python编程书: 89.0元 - 无线鼠标: 129.0元 - 机械键盘: 399.0元 原价: 617.0元 折后价: 567.0元 节省: 50.0元 === 季节性优惠 === 购物车内容: - Python编程书: 89.0元 - 无线鼠标: 129.0元 - 机械键盘: 399.0元 原价: 617.0元 折后价: 572.0元 节省: 45.0元
Fortgeschrittene Nutzung des Strategie-Musters
1. Strategie-Fabrikmuster
Kombiniere das Fabrikmuster, um die Erstellung von Strategien zu verwalten:
Beispiel
"""Rabattstrategie-Fabrik"""
@staticmethod
def create_strategy(strategy_type: str, **kwargs) -> DiscountStrategy:
"""Rabattstrategie erstellen"""
strategies = {
"no_discount": NoDiscountStrategy,
"percentage": PercentageDiscountStrategy,
"fixed_amount": FixedAmountDiscountStrategy,
"seasonal": SeasonalDiscountStrategy
}
if strategy_type not in strategies:
raise ValueError(f"Nicht unterstützter Strategietyp: {strategy_type}")
return strategies[strategy_type](**kwargs)
# Fabrikmuster verwenden
factory = DiscountStrategyFactory()
# Verschiedene Strategien erstellen
strategy1 = factory.create_strategy("percentage", percentage=15) # 15% Rabatt
strategy2 = factory.create_strategy("fixed_amount", discount_amount=100) # 100 yuan Rabatt
2. Dynamische Strategieauswahl
Strategien dynamisch anhand von Bedingungen auswählen:
Beispiel
"""Dynamischer Rabattselektor"""
@staticmethod
def select_strategy(user_type: str, total_amount: float) -> DiscountStrategy:
"""Strategie basierend auf Benutzertyp und Gesamtbetrag auswählen"""
if user_type == "vip":
if total_amount > 500:
return PercentageDiscountStrategy(25) # VIP: 25% Rabatt auf Bestellungen ab 500 oder mehr
else:
return PercentageDiscountStrategy(15) # VIP: 15% Rabatt
elif user_type == "normal":
if total_amount > 300:
return FixedAmountDiscountStrategy(30) # Normale Benutzer: 30 Yuan Rabatt auf Bestellungen ab 300 oder mehr
else:
return NoDiscountStrategy()
else:
return NoDiscountStrategy()
# Dynamische Auswahl verwenden
cart = ShoppingCart()
cart.add_item("Produkt A", 200)
cart.add_item("Product B", 150)
selector = DynamicDiscountSelector()
strategy = selector.select_strategy("vip", cart.calculate_total())
cart.set_discount_strategy(strategy)
cart.display_cart()
Vorteile und Anwendungsszenarien des Strategiemusters
Vorteilsvergleich
| Merkmal | Traditioneller Ansatz | Strategie-Muster |
|---|---|---|
| Erweiterbarkeit | Erfordert die Änderung vorhandenen Codes | Einfach eine neue Strategieklasse hinzufügen |
| Wartbarkeit | Hohe Code-Kopplung | Trennung von Zuständigkeiten, einfach zu warten |
| Flexibilität | Schwierig, Algorithmen zur Laufzeit zu wechseln | Strategien können dynamisch gewechselt werden |
| Testbarkeit | Schwierig, Algorithmen isoliert zu testen | Jede Strategie kann unabhängig getestet werden |
Geeignete Szenarien
- Mehrere Algorithmusvarianten: Wenn Sie mehrere ähnliche Klassen haben, die sich nur in bestimmten Verhaltensweisen unterscheiden.
- Bedingte Anweisungen vermeiden: Wenn Sie vermeiden möchten, eine große Anzahl bedingter Anweisungen (if-else oder switch) zu verwenden.
- Algorithmusauswahl zur Laufzeit: Wenn Sie zur Laufzeit verschiedene Algorithmen auswählen müssen.
- Algorithmuskapselung: Wenn Sie Algorithmusdetails von den Clients isolieren möchten, die den Algorithmus verwenden.
Ungeeignete Szenarien
- Einfache Algorithmen: Wenn es nur einen oder zwei Algorithmen gibt, die sich selten ändern, kann es Over-Engineering sein.
- Client muss Strategiedetails kennen: Wenn der Client die konkrete Implementierung der Strategie kennen muss.
- Zu viele Strategien: Wenn die Anzahl der Strategieklassen explodiert, sollten Sie andere Muster in Betracht ziehen.
Best Practices und Überlegungen
Vorschläge zur Codeorganisation
Beispiel
project/
├── strategies/
│ ├── __init__.py
│ ├── base_strategy.py # Basisstrategie-Schnittstelle
│ ├── discount_strategies.py # Rabattbezogene Strategien
│ └── payment_strategies.py # Zahlungsbezogene Strategien
├── contexts/
│ ├── __init__.py
│ └── shopping_cart.py # Kontextklasse
└── main.py
Fehlerbehandlung
Beispiel
"""Rabattstrategie mit Fehlerbehandlung"""
def __init__(self, base_strategy: DiscountStrategy, fallback_strategy: DiscountStrategy = Keine):
self.base_strategy = base_strategy
self.fallback_strategy = fallback_strategy or NoDiscountStrategy()
def calculate_discount(self, original_price: float) -> float:
try:
return self.base_strategy.calculate_discount(original_price)
except Exception as e:
print(f"Fehler bei der Rabattberechnung: {e}, Fallback-Strategie wird verwendet")
return self.fallback_strategy.calculate_discount(original_price)
Leistungsüberlegungen
Für leistungssensible Szenarien sollten die folgenden Optimierungen in Betracht gezogen werden:
Beispiel
class CachedDiscountStrategy(DiscountStrategy):
"""Rabattstrategie mit Caching"""
def __init__(self, base_strategy: DiscountStrategy):
self.base_strategy = base_strategy
@lru_cache(maxsize=128)
def calculate_discount(self, original_price: float) -> float:
return self.base_strategy.calculate_discount(original_price)
Praktische Übungen
Übung 1: Implementierung der Sortierstrategie
Implementieren Sie einen Sortierer, der mehrere Sortieralgorithmen unterstützt:
Beispiel
from typing import List
class SortStrategy(ABC):
@abstractmethod
def sort(self, data: List) -> List:
pass
# TODO: Bubble-Sort-Strategie implementieren
class BubbleSortStrategy(SortStrategy):
def sort(self, data: List) -> List:
# Dein Implementierungscode
pass
# TODO: Quick-Sort-Strategie implementieren
class QuickSortStrategy(SortStrategy):
def sort(self, data: List) -> List:
# Dein Implementierungscode
pass
# TODO: Merge-Sort-Strategie implementieren
class MergeSortStrategy(SortStrategy):
def sort(self, data: List) -> List:
# Dein Implementierungscode
pass
class Sorter:
def __init__(self, strategy: SortStrategy = None):
self._strategy = strategy
def set_strategy(self, strategy: SortStrategy):
self._strategy = strategy
def sort_data(self, data: List) -> List:
if not self._strategy:
raise ValueError("Sortierstrategie nicht gesetzt")
return self._strategy.sort(data)
# Testen Sie Ihre Implementierung
data = [64, 34, 25, 12, 22, 11, 90]
sorter = Sorter(BubbleSortStrategy())
result = sorter.sort_data(data)
print(f"Sortierergebnis: {result}")
Übung 2: Dateikomprimierungsstrategie
Entwerfen Sie einen Dateikompressor, der mehrere Komprimierungsformate unterstützt:
Beispiel
class CompressionStrategy(ABC):
@abstractmethod
def compress(self, file_path: str) -> str:
pass
@abstractmethod
def decompress(self, file_path: str) -> str:
pass
# TODO: ZIP-Komprimierungsstrategie implementieren
class ZipCompressionStrategy(CompressionStrategy):
def compress(self, file_path: str) -> str:
# Dein Implementierungscode
pass
def decompress(self, file_path: str) -> str:
# Dein Implementierungscode
pass
# TODO: GZIP-Komprimierungsstrategie implementieren
class GzipCompressionStrategy(CompressionStrategy):
def compress(self, file_path: str) -> str:
# Dein Implementierungscode
pass
def decompress(self, file_path: str) -> str:
# Dein Implementierungscode
pass
class FileCompressor:
def __init__(self, strategy: CompressionStrategy = None):
self._strategy = strategy
def set_strategy(self, strategy: CompressionStrategy):
self._strategy = strategy
def compress_file(self, file_path: str) -> str:
if not self._strategy:
raise ValueError("Komprimierungsstrategie nicht festgelegt")
return self._strategy.compress(file_path)
def decompress_file(self, file_path: str) -> str:
if not self._strategy:
raise ValueError("Komprimierungsstrategie nicht festgelegt")
return self._strategy.decompress(file_path)
Zusammenfassung
Das Strategie-Muster ist ein sehr praktisches Entwurfsmuster in Python. Durch die Kapselung von Algorithmen in unabhängige Strategieklassen bietet es gute Erweiterbarkeit und Flexibilität. Nach dem Studium dieses Artikels sollten Sie in der Lage sein:
- die Kernkonzepte und Einsatzszenarien des Strategie-Musters verstehen
- Beherrsche die grundlegenden Implementierungsmethoden des Strategie-Musters
- Wende das Strategie-Muster in realen Projekten angemessen an
- Vermeide häufige Fehlanwendungen des Strategie-Musters