Strategy-Muster
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 93 von 110.
Das Strategy-Pattern ermöglicht es Ihnen, eine Familie von Algorithmen zu definieren, jeden einzelnen in einer eigenen Klasse zu kapseln und sie zur Laufzeit austauschbar zu machen. Anstatt das Verhalten fest in eine Klasse zu kodieren, injizieren Sie das Verhalten über eine Schnittstelle, was es Ihnen ermöglicht, Strategien auszutauschen, ohne die Klasse, die sie verwendet, zu ändern.
Stellen Sie sich ein Zahlungssystem vor, bei dem Sie verschiedene Zahlungsmethoden unterstützen möchten. Anstatt Bedingungen zu verwenden, um jede Methode zu handhaben, definieren Sie eine Strategie-Schnittstelle und erstellen konkrete Implementierungen:
abstract class PaymentStrategy {
void pay(double amount);
}
class CreditCardPayment implements PaymentStrategy {
@override
void pay(double amount) => print('Paid \$amount with Credit Card');
}
class PayPalPayment implements PaymentStrategy {
@override
void pay(double amount) => print('Paid \$amount with PayPal');
}
class ShoppingCart {
PaymentStrategy _paymentStrategy;
ShoppingCart(this._paymentStrategy);
void setPaymentStrategy(PaymentStrategy strategy) {
_paymentStrategy = strategy;
}
void checkout(double total) {
_paymentStrategy.pay(total);
}
}
void main() {
var cart = ShoppingCart(CreditCardPayment());
cart.checkout(99.99); // 99.99 $ mit Kreditkarte bezahlt
cart.setPaymentStrategy(PayPalPayment());
cart.checkout(49.99); // 49.99 $ mit PayPal bezahlt
}Der ShoppingCart weiß nicht und es ist ihm egal, wie die Zahlung funktioniert – er delegiert einfach an die Strategie, die aktuell eingestellt ist. Sie können neue Zahlungsmethoden hinzufügen, indem Sie neue Strategie-Klassen erstellen, ohne den Code des Warenkorbs anzupassen. Dieses Muster ist ideal, wenn Sie mehrere Möglichkeiten haben, eine Aktion auszuführen, und dynamisch zwischen ihnen wählen oder wechseln möchten.
Aufgabe
EinfachLassen Sie uns ein Textformatierungssystem mit dem Strategy-Pattern erstellen! Sie werden ein System entwickeln, bei dem verschiedene Formatierungsstrategien auf Text angewendet werden können, sodass Benutzer dynamisch zwischen Formaten wechseln können, ohne den Kern des Textprozessors zu ändern.
Sie werden Ihren Code in zwei Dateien organisieren:
formatting_strategies.dart: Erstellen Sie hier Ihr Strategiesystem. Definieren Sie eine abstrakte KlasseFormattingStrategymit einer MethodeString format(String text). Implementieren Sie dann drei konkrete Strategien:UppercaseStrategy- wandelt Text in Großbuchstaben umLowercaseStrategy- wandelt Text in Kleinbuchstaben umTitleCaseStrategy- schreibt den ersten Buchstaben jedes Wortes groß (nach Leerzeichen trennen, das erste Zeichen jedes Wortes großschreiben, den Rest klein)
TextProcessor-Klasse, die eineFormattingStrategyhält. Sie sollte einen Konstruktor haben, der eine initiale Strategie akzeptiert, einesetStrategy()-Methode, um die Strategie zur Laufzeit zu ändern, und eineprocess(String text)-Methode, die die aktuelle Strategie anwendet und das formatierte Ergebnis zurückgibt.main.dart: Importieren Sie Ihre Formatierungsstrategien und demonstrieren Sie, wie das Strategy-Pattern den dynamischen Wechsel des Verhaltens ermöglicht. Erstellen Sie einenTextProcessor, der mit derUppercaseStrategybeginnt. Verarbeiten Sie den Text'hello world'und geben Sie das Ergebnis aus. Wechseln Sie dann zurLowercaseStrategy, verarbeiten Sie'HELLO WORLD'und geben Sie das Ergebnis aus. Wechseln Sie schließlich zurTitleCaseStrategy, verarbeiten Sie'the quick brown fox'und geben Sie das Ergebnis aus.
Dies demonstriert die Stärke des Strategy-Patterns – Ihr TextProcessor muss die Details der Formatierung nicht kennen. Er delegiert einfach an die aktuell eingestellte Strategie, was es einfach macht, neue Formatierungsoptionen hinzuzufügen, ohne den Prozessor zu modifizieren!
Erwartete Ausgabe:
HELLO WORLD
hello world
The Quick Brown FoxSpickzettel
Das Strategy-Muster definiert eine Familie von Algorithmen, kapselt jeden einzelnen in einer eigenen Klasse und macht sie zur Laufzeit austauschbar. Anstatt das Verhalten fest zu kodieren, injizieren Sie es über eine Schnittstelle.
Definieren Sie ein Strategy-Interface:
abstract class PaymentStrategy {
void pay(double amount);
}Erstellen Sie konkrete Strategy-Implementierungen:
class CreditCardPayment implements PaymentStrategy {
@override
void pay(double amount) => print('Paid \$amount with Credit Card');
}
class PayPalPayment implements PaymentStrategy {
@override
void pay(double amount) => print('Paid \$amount with PayPal');
}Erstellen Sie eine Kontext-Klasse, die die Strategy verwendet:
class ShoppingCart {
PaymentStrategy _paymentStrategy;
ShoppingCart(this._paymentStrategy);
void setPaymentStrategy(PaymentStrategy strategy) {
_paymentStrategy = strategy;
}
void checkout(double total) {
_paymentStrategy.pay(total);
}
}Verwenden Sie das Muster, indem Sie Strategien injizieren und wechseln:
var cart = ShoppingCart(CreditCardPayment());
cart.checkout(99.99); // Bezahlt $99.99 mit Kreditkarte
cart.setPaymentStrategy(PayPalPayment());
cart.checkout(49.99); // Bezahlt $49.99 mit PayPalDie Kontext-Klasse delegiert das Verhalten an die Strategy, ohne Implementierungsdetails zu kennen. Fügen Sie neue Strategien hinzu, indem Sie neue Klassen erstellen, ohne bestehenden Code zu ändern.
Probier es selbst
import 'formatting_strategies.dart';
void main() {
// TODO: Erstelle einen TextProcessor, der mit UppercaseStrategy beginnt
// TODO: Verarbeite den Text 'hello world' und gib das Ergebnis aus
// TODO: Wechsle zu LowercaseStrategy
// TODO: Verarbeite 'HELLO WORLD' und gib das Ergebnis aus
// TODO: Wechsle zu TitleCaseStrategy
// TODO: Verarbeite 'the quick brown fox' und gib das Ergebnis aus
}
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 DateienBibliotheken & ImportsEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenInstanzvariablenKonstruktor-GrundlagenWiederholung – Einfacher Taschenrechner4Null-Sicherheit
Einführung in Null-SicherheitNullable vs. Non-NullableDie ? und ! OperatorenLate-Schlüsselwort & Null-SicherheitNull-aware OperatorenNull-Sicherheit in KlassenZusammenfassung – Benutzerprofil-System7Abstrakte Klassen & Interfaces
Abstrakte KlassenAbstrakte MethodenInterfaces in DartImplizite InterfacesImplementieren vs. ErweiternMehrfache InterfacesRückblick – Shape Calculator10Collections & Generics
Überblick über List, Set und MapTypsichere CollectionsGenerische KlassenGenerische MethodenGenerische ConstraintsIterable & IteratorZusammenfassung – Generische Speicherung13Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums mit Methoden16Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen2Konstruktoren in Dart
Standard-KonstruktorBenannte KonstruktorenInitialisierungslistenKonstante KonstruktorenFactory-KonstruktorenWeiterleitende KonstruktorenZusammenfassung – Shape Builder5Kapselung
Öffentliche vs. private MemberDie _ Präfix-KonventionSichtbarkeit auf Library-EbeneGetter & Setter im DetailInformation HidingRückblick – Schülerdatensätze8Mixins
Einführung in MixinsMixins erstellenMehrere Mixins verwendenDas on-Schlüsselwort in MixinsMixin vs. VererbungMixin vs. InterfaceZusammenfassung - Tiersystem11Spezielle Methoden
toString() überschreibenhashCode & == überschreibenComparable-Interfacecall()-MethodenoSuchMethod überschreibenZusammenfassung – Eigene Collection14Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster3Klasseneigenschaften
Instanz- vs. statische MemberFinal & Const FelderLate-VariablenStatische Methoden & FelderGetter und SetterZusammenfassung – Bankkonto-Manager6Vererbung
Grundlagen der VererbungDas super-SchlüsselwortMethoden überschreibenDie @override-AnnotationDas final-Klassen-SchlüsselwortKonstruktoren & VererbungRückblick – Mitarbeiter-Hierarchie9Polymorphismus
Polymorphismus-GrundlagenPolymorphismus über InterfacesTypprüfung zur LaufzeitDie is & as OperatorenDas covariant SchlüsselwortZusammenfassung – Payment Processor