Wzorzec Strategy
Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 69 z 87.
Wzorzec strategii to behawioralny wzorzec projektowy, który pozwala zdefiniować rodzinę algorytmów, zamknąć każdy z nich we własnej klasie i sprawić, by można je było wzajemnie zastępować. Zamiast zakodowywać na stałe określone zachowanie, możesz w czasie działania programu zamieniać algorytmy bez zmieniania kodu, który z nich korzysta.
Wyobraź sobie system płatności obsługujący wiele metod płatności. Bez wzorca Strategia skończyłoby się na nieuporządkowanej logice warunkowej. Dzięki niemu każda metoda płatności staje się własną strategią:
interface PaymentStrategy {
void pay(int amount);
}
class CreditCardPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " via Credit Card");
}
}
class PayPalPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " via PayPal");
}
}Klasa kontekstu przechowuje odwołanie do strategii i deleguje jej wykonanie. Kluczowe jest to, że kontekst nie wie, której konkretnej strategii używa. Zna jedynie interfejs:
class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(int amount) {
paymentStrategy.pay(amount);
}
}Teraz możesz dynamicznie zmieniać sposób płatności:
ShoppingCart cart = new ShoppingCart();
cart.setPaymentStrategy(new CreditCardPayment());
cart.checkout(100); // Zapłacono 100 kartą kredytową
cart.setPaymentStrategy(new PayPalPayment());
cart.checkout(50); // Zapłacono 50 przez PayPalWzorzec Strategii sprawdza się, gdy masz wiele sposobów wykonania działania i chcesz łatwo się między nimi przełączać. Dodanie nowej metody płatności oznacza po prostu utworzenie nowej klasy implementującej PaymentStrategy: nie trzeba wprowadzać żadnych zmian w istniejącym kodzie.
Wyzwanie
ŁatwyZbudujmy system formatowania tekstu z wykorzystaniem wzorca Strategia! Utworzysz system, w którym różne strategie formatowania można wymieniać w czasie działania programu, aby przekształcać tekst na różne sposoby — idealne rozwiązanie dla aplikacji, która musi obsługiwać wiele formatów wyjściowych.
Rozmieścisz kod w czterech plikach:
TextFormatter.java: Zdefiniuj interfejsTextFormatter, który będą implementować wszystkie strategie formatowania. Powinien deklarować jedną metodęString format(String text), która przyjmuje ciąg znaków i zwraca jego sformatowaną wersję.Formatters.java: Utwórz trzy konkretne strategie implementujące interfejsTextFormatter:UpperCaseFormatter— przekształca tekst, zamieniając wszystkie litery na wielkie.LowerCaseFormatter— przekształca tekst, zamieniając wszystkie litery na małe.TitleCaseFormatter— zamienia pierwszą literę każdego słowa na wielką, a pozostałe na małe. Słowa są oddzielone spacjami.TextEditor.java: Utwórz klasę kontekstu korzystającą ze strategii formatowania. KlasaTextEditorpowinna mieć:Prywatne pole przechowujące bieżącą strategię
TextFormatter.Metodę
setFormatter(TextFormatter formatter), która zmienia aktywną strategię formatowania.Metodę
publishText(String text), która stosuje bieżący formater do tekstu i wyświetlaPublished: [formatted text]. Jeśli nie ustawiono żadnego formatera, powinna wyświetlić niezmieniony tekstPublished: [original text].Main.java: Połącz elementy systemu strategii! Otrzymasz dwa dane wejściowe: tekst do sformatowania (String) i styl formatowania (String:"upper","lower"lub"title").Utwórz obiekt
TextEditori najpierw wywołajpublishTextz tekstem wejściowym, zanim ustawisz jakikolwiek formater (demonstrując domyślne działanie).Następnie, w zależności od podanego stylu, ustaw odpowiednią strategię formatowania i ponownie wywołaj
publishTextz tym samym tekstem, aby wyświetlić sformatowany wynik.
Otrzymasz po kolei dwa dane wejściowe: tekst do sformatowania (String) i styl formatowania (String).
Na przykład dla danych wejściowych hello world i title wynik będzie następujący:
Published: hello world
Published: Hello WorldZauważ, że TextEditor nie wie nic o szczegółach logiki formatowania. Po prostu przekazuje zadanie aktualnie ustawionej strategii. Dzięki temu dodawanie nowych stylów formatowania bez modyfikowania kodu edytora jest niezwykle proste!
Spróbuj swoich sił
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String text = scanner.nextLine();
String style = scanner.nextLine();
// TODO: Utwórz instancję TextEditor
// TODO: Wywołaj publishText z tekstem wejściowym (przed ustawieniem formatowania)
// TODO: Na podstawie podanego stylu ("upper", "lower" lub "title"),
// ustaw odpowiednie formatowanie i ponownie wywołaj publishText
}
}Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektySłowo kluczowe thisMetodyPola (atrybuty)Metoda konstruktoraPrzeciążanie konstruktoraPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawy dziedziczenia (extends)Słowo kluczowe superPrzesłanianie metod (@Override)Łańcuch wywołań konstruktorówKlasa ObjectDziedziczenie jedno- i wielopoziomoweDlaczego Java nie obsługuje wielodziedziczenia klasPodsumowanie – hierarchia pracowników7Metody specjalne i klasa Object
Metoda toString()equals() i hashCode()Metoda clone()compareTo() i ComparableInterfejs ComparatorPodsumowanie — własne sortowanie10Obsługa wyjątków w programowaniu obiektowym
Hierarchia klas wyjątkówWłasne wyjątkiBłędy sprawdzane a niesprawdzaneWzorzec try-with-resourcesPodsumowanie – zweryfikowany użytkownik2Modyfikatory dostępu i enkapsulacja
Przegląd poziomów dostępuMetody pobierające i ustawiająceUkrywanie informacjiSłowo kluczowe finalPodsumowanie — menedżer kont bankowych5Polimorfizm
Podstawy przeciążania metodPrzesłanianie metod (w czasie wykonywania)Rzutowanie w górę i w dółOperator instanceofKlasy i metody abstrakcyjnePowtórka — kalkulator figur8Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieAgregacja a kompozycjaKlasy wewnętrzne, zagnieżdżone i anonimoweEnumy i metody enumówRekordy (Java 16+)Klasy zapieczętowane (Java 17+)11Wzorce projektowe — część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec BuilderWzorzec ObserverWzorzec Strategy3Właściwości klas i składowe statyczne
Zmienne instancji a zmienne statyczneMetody statyczneBloki statyczneStałe (static final)Powtórka – Counter i Utility6Interfejsy i klasy abstrakcyjne
Wprowadzenie do interfejsówImplementowanie interfejsówImplementacja wielu interfejsówMetody domyślne i statyczne w interfejsieKlasy abstrakcyjne a interfejsyInterfejsy funkcyjnePodsumowanie — system płatnościPoćwicz samodzielnie: Kompilator Java online