Menu
CoddyTech

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 * 2

Kontekst 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 20

Fighter 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
55

Uż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.

challenge icon

Wyzwanie

Średni

Koszyk 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) zwraca total - total * percent / 100 (arytmetyka całkowitoliczbowa), a label() zwraca 10% off dla 10 procent.
  • FixedOff (tworzony z kwotą): apply(total) zwraca sumę pomniejszoną o kwotę, ale nigdy mniej niż 0, a label() zwraca 30 off dla kwoty 30.
  • Cart: set_discount(new_discount) zapisuje strategię, a checkout() sumuje ceny i zwraca Subtotal TOTAL, LABEL, pay PRICE, gdzie PRICE to wartość zwracana przez apply() 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 120

Umieść 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())
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator GDScript online