Strategy-Muster
Teil des Abschnitts Objektorientierte Programmierung der PHP-Journey von Coddy. Lektion 74 von 91.
Das Strategy Pattern ist ein Verhaltensentwurfsmuster, mit dem du eine Familie von Algorithmen definieren, jeden einzelnen in seiner eigenen Klasse kapseln und sie zur Laufzeit austauschbar machen kannst. Das Objekt, das die Strategie verwendet, muss nicht wissen, welchen Algorithmus es ausführt.
Stell dir einen Einkaufswagen vor, der verschiedene Rabattstrategien anwenden kann. Statt die Rabattlogik mit Bedingungen fest zu codieren, definierst du jeden Rabatt als separate Klasse:
<?php
interface DiscountStrategy {
public function calculate(float $total): float;
}
class NoDiscount implements DiscountStrategy {
public function calculate(float $total): float {
return $total;
}
}
class PercentageDiscount implements DiscountStrategy {
public function __construct(private int $percent) {}
public function calculate(float $total): float {
return $total - ($total * $this->percent / 100);
}
}
class FixedDiscount implements DiscountStrategy {
public function __construct(private float $amount) {}
public function calculate(float $total): float {
return max(0, $total - $this->amount);
}
}
Die Kontextklasse akzeptiert jede Strategie und delegiert die Berechnung:
<?php
class ShoppingCart {
public function __construct(private DiscountStrategy $discount) {}
public function setDiscount(DiscountStrategy $discount): void {
$this->discount = $discount;
}
public function checkout(float $total): float {
return $this->discount->calculate($total);
}
}
$cart = new ShoppingCart(new NoDiscount());
echo $cart->checkout(100) . "\n";
$cart->setDiscount(new PercentageDiscount(20));
echo $cart->checkout(100);
Ausgabe:
100
80Das Strategy Pattern glänzt, wenn du mehrere Möglichkeiten hast, eine Operation auszuführen, und zwischen ihnen wechseln möchtest, ohne die Klasse zu ändern, die sie verwendet. Das Hinzufügen eines neuen Rabatttyps bedeutet, eine neue Klasse zu erstellen, statt vorhandenen Code anzufassen.
Aufgabe
EinfachErstellen wir einen Versandkostenrechner mit dem Strategiemuster. Verschiedene Versandmethoden haben unterschiedliche Preisregeln: Der Standardversand berechnet einen Pauschalpreis, der Expressversand addiert einen Prozentsatz, und der Übernachtversand kombiniert beides. Mit dem Strategiemuster kannst du diese Berechnungen austauschen, ohne den Code zu ändern, der sie verwendet.
Du organisierst deinen Code auf vier Dateien verteilt:
ShippingStrategy.php: Erstelle einShippingStrategy-Interface, das den Vertrag für alle Versandmethoden definiert. Es sollte eine Methodecalculate(float $weight): floatenthalten, die das Paketgewicht entgegennimmt und die Versandkosten zurückgibt.ShippingMethods.php: Binde die Datei ShippingStrategy ein und erstelle drei Klassen, die das Interface implementieren:StandardShipping: Berechnet unabhängig vom Gewicht einen Pauschalpreis von$5.00ExpressShipping: Berechnet$2.00pro Kilogramm (multipliziert das Gewicht mit 2)OvernightShipping: Berechnet eine Grundgebühr von$10.00plus$3.00pro Kilogramm
ShippingCalculator.php: Binde die Datei ShippingStrategy ein und erstelle eine KlasseShippingCalculator, die als Kontext fungiert. Dein Rechner sollte:- In seinem Konstruktor ein
ShippingStrategyakzeptieren (verwende Konstruktor-Promotion) - Eine Methode
setStrategy(ShippingStrategy $strategy): voidbesitzen, um die Versandmethode zur Laufzeit zu ändern - Eine Methode
calculateCost(float $weight): floatbesitzen, die die Berechnung an die aktuelle Strategie delegiert
- In seinem Konstruktor ein
main.php: Binde die Dateien ShippingMethods und ShippingCalculator ein. Du erhältst zwei Eingaben: einen Versandtyp ("standard","express"oder"overnight") und ein Paketgewicht.Erstelle einen
ShippingCalculator, der mitStandardShippingbeginnt. Verwende abhängig von der Eingabe des VersandtypssetStrategy(), um zur passenden Versandmethode zu wechseln. Berechne anschließend die Kosten für das angegebene Gewicht und gib das Ergebnis mit zwei Nachkommastellen formatiert aus.Die Ausgabe sollte lauten:
Shipping cost: $[cost]
Diese Aufgabe zeigt, wie das Strategiemuster dein Versandsystem flexibel macht: Das Hinzufügen einer neuen Versandmethode wie SameDayShipping erfordert lediglich das Erstellen einer neuen Klasse, die das Interface implementiert, ohne den Rechner oder bestehende Versandmethoden zu ändern.
Probier es selbst
<?php
require_once 'ShippingMethods.php';
require_once 'ShippingCalculator.php';
// Eingabe lesen
$shippingType = trim(fgets(STDIN));
$weight = floatval(fgets(STDIN));
// TODO: Erstelle einen ShippingCalculator beginnend mit StandardShipping
// TODO: Basierend auf der Versandtyp-Eingabe ("standard", "express", or "overnight"),
// verwende setStrategy(), um zur entsprechenden Versandmethode zu wechseln
// TODO: Berechne die Kosten für das gegebene Gewicht
// TODO: Gib das Ergebnis formatiert auf zwei Dezimalstellen aus
// Output format: Shipping cost: $[cost]
?>Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in OOPKlassen vs. ObjekteDas Schlüsselwort $thisMethodenEigenschaftenKonstruktor (__construct)Destruktor (__destruct)Rückblick – Einfacher Taschenrechner4Vererbung
Grundlegende VererbungDas Schlüsselwort parent::MethodenüberschreibungDas Schlüsselwort finalAbstrakte KlassenRückblick – Mitarbeiterhierarchie7Kapselung
Öffentlich, geschützt, privatZugriffsmodifikatoren im DetailGetter und SetterInformationsverbergungKonstruktor-Promotion (8.0)Wiederholung – System zur Verwaltung von Schülerdatensätzen2Namespaces & Autoloading
Einführung in NamespacesDas use-SchlüsselwortPSR-4-Autoloading-StandardComposer-AutoloaderRückblick – Organisiertes Projekt5Schnittstellen & Verträge
Einführung in SchnittstellenSchnittstellen implementierenMehrere Schnittstellen implementierenSchnittstelle vs. abstrakte KlasseTyp-Hinweise mit SchnittstellenRückblick – Formrechner8Magische Methoden
Einführung in magische Methoden__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Objektklonen__serialize & __unserializeRückblick – Benutzerdefinierte Sammlung11Typsystem & Fehlerbehandlung
TypdeklarationenNullable-TypenUnion- & Intersection-TypenException-KlassenBenutzerdefinierte Exception-HierarchieTry, Catch, FinallyRückblick – Formularvalidator14Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen3Klassen-Eigenschaften
Instanz- vs. statische EigenschaftenKonstanten in KlassenStatische Methoden & EigenschaftenPrivate & geschützte EigenschaftenSchreibgeschützte Eigenschaften (PHP 8.1)Zusammenfassung – Bankkonto-Manager6Polymorphie
Methodenüberschreibung erneut betrachtetPolymorphie über SchnittstellenTyp-Hinweise & Union-TypenSpätes statisches BindenRückblick – Zahlungsabwickler9Traits
Einführung in TraitsMehrere Traits verwendenAuflösung von Trait-KonfliktenAbstrakte Methoden in TraitsTraits vs. Vererbung12Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster15Abschließende Herausforderungen
E-Learning-PlattformBankingsystemEntwicklung einer SpielfigurFahrzeugvermietungÜbe selbstständig: Online-PHP-Compiler