Wzorzec Strategy
Część sekcji Programowanie obiektowe ścieżki GDScript w Coddy. Lekcja 49 z 65.
Wzorzec Strategia umieszcza każdą wersję algorytmu we własnym obiekcie, dzięki czemu możesz zmieniać algorytm podczas działania programu. Obiekt, który z niego korzysta — kontekst — wywołuje jedną metodę i nie musi wiedzieć, którą wersję ma.
Oto dwie strategie ataku: klasa nadrzędna i podklasa z tą samą metodą damage():
# attack_style.gd
class_name AttackStyle
extends RefCounted
func damage(power: int) -> int:
return power# heavy_attack.gd
class_name HeavyAttack
extends AttackStyle
func damage(power: int) -> int:
return power * 2Kontekst przechowuje strategię i przekazuje jej obliczenie:
# fighter.gd
class_name Fighter
extends RefCounted
var power := 10
var style: AttackStyle = AttackStyle.new()
func set_style(new_style: AttackStyle) -> void:
style = new_style
func attack() -> void:
print("Hit for ", style.damage(power))# main.gd
var fighter := Fighter.new()
fighter.attack()
fighter.set_style(HeavyAttack.new())
fighter.attack()Hit for 10
Hit for 20Fighter nigdy nie sprawdza, jaki styl posiada. Nowy styl to nowa klasa, a fighter.gd pozostaje bez zmian; łańcuch if style == "heavy" wewnątrz attack() rozrastałby się z każdym nowym stylem.
Gdy strategia jest jedną niewielką funkcją bez własnych danych, wystarczy Callable. Kontekst przechowuje funkcję i uruchamia ją za pomocą call():
# price_calculator.gd
class_name PriceCalculator
extends RefCounted
var discount: Callable = func(price): return price
func final_price(price: int) -> int:
return discount.call(price)# main.gd
var calc := PriceCalculator.new()
print(calc.final_price(80))
calc.discount = func(price): return price / 2
print(calc.final_price(80))
calc.discount = func(price): return max(price - 25, 0)
print(calc.final_price(80))80
40
55Używaj klas, gdy strategia ma własne ustawienia lub kilka metod, a obiektów wywoływalnych — do jednego krótkiego obliczenia. Korzystałeś już wcześniej z tego pomysłu: funkcja porównująca, którą przekazujesz do sort_custom(), jest strategią sortowania.
Wyzwanie
ŚredniKoszyk sklepowy może zmieniać strategie zniżek przy finalizacji zakupu. Discount w discount.gd to strategia bazowa: brak zniżki. Napisz dwie kolejne oraz koszyk:
PercentOff(tworzony z wartością procentową):apply(total)zwracatotal - total * percent / 100(arytmetyka całkowitoliczbowa), alabel()zwraca10% offdla 10 procent.FixedOff(tworzony z kwotą):apply(total)zwraca sumę pomniejszoną o kwotę, ale nigdy mniej niż0, alabel()zwraca30 offdla kwoty 30.Cart:set_discount(new_discount)zapisuje strategię, acheckout()sumuje ceny i zwracaSubtotal TOTAL, LABEL, pay PRICE, gdzie PRICE to wartość zwracana przezapply()strategii.
Dostarczony kod odczytuje ceny w jednym wierszu, liczbę kolejnych finalizacji zakupu oraz jedną strategię w każdym wierszu: percent N, fixed N lub none. Dla cen 50 30 40 i strategii percent 10, fixed 30, none wynik to:
Subtotal 120, 10% off, pay 108
Subtotal 120, 30 off, pay 90
Subtotal 120, no discount, pay 120Umieść swój kod w plikach cart.gd, percent_off.gd i fixed_off.gd. main.gd zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
extends Node
func _ready():
# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
var cart := Cart.new()
for price in OS.read_string_from_stdin().split(" "):
cart.add_item(int(price))
var count := int(OS.read_string_from_stdin())
for i in count:
var parts := OS.read_string_from_stdin().split(" ")
if parts[0] == "percent":
cart.set_discount(PercentOff.new(int(parts[1])))
elif parts[0] == "fixed":
cart.set_discount(FixedOff.new(int(parts[1])))
else:
cart.set_discount(Discount.new())
print(cart.checkout())
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 selfMetodyWłaściwościKonstruktor (_init)Powtórka – prosty kalkulator4Polimorfizm
Ponowne omówienie przesłaniania metodDuck typingKlasy i metody abstrakcyjneProjektowanie interfejsówPowtórzenie – kalkulator kształtów7Sygnały
Czym jest sygnał?Deklarowanie i emitowanieŁączenie z sygnałamiSygnały z argumentamiPodsumowanie — system zdrowia10Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytu2Właściwości klas
Zmienne instancji a zmienne statyczneFunkcje statyczneSettery właściwościGettery właściwościPrywatne z konwencjiPodsumowanie — menedżer kont bankowych5Enkapsulacja
Elementy publiczne i prywatneWalidacja za pomocą setterówUkrywanie informacjiWłaściwości tylko do odczytuPodsumowanie – system ewidencji uczniów8Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieKlasy wewnętrzneWęzły jako komponentyRefCounted a NodeTypy wyliczeniowe w klasach11Projekt: Zarządzanie biblioteką
Przegląd projektuKlasy książki i użytkownika3Dziedziczenie
Podstawy dziedziczeniaSłowo kluczowe superNadpisywanie metodŁańcuchy dziedziczeniaSprawdzanie typów za pomocą isPodsumowanie – hierarchia pracowników6Metody specjalne
Wprowadzenie do metod wirtualnychMetoda _to_stringWłasne iteratoryPodsumowanie — własna lista9Wzorce projektowe — część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec StrategyPoćwicz samodzielnie: Kompilator GDScript online