Pattern Strategy
Fa parte della sezione Programmazione orientata agli oggetti del percorso GDScript di Coddy. Lezione 49 di 65.
Il pattern Strategy inserisce ogni versione di un algoritmo nel proprio oggetto, così puoi sostituire l’algoritmo mentre il programma è in esecuzione. L’oggetto che lo utilizza, il contesto, chiama un metodo e non gli importa quale versione contiene.
Ecco due strategie di attacco, una classe padre e una sottoclasse con lo stesso metodo 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 * 2Il contesto memorizza una strategia e le affida il calcolo:
# 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 non controlla mai quale stile usa. Un nuovo stile corrisponde a una nuova classe, mentre fighter.gd rimane così com'è; una catena if style == "heavy" all'interno di attack() crescerebbe con ogni stile.
Quando una strategia consiste in una piccola funzione senza dati propri, è sufficiente un Callable. Il contesto memorizza la funzione e la esegue con 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
55Usa le classi quando una strategia ha impostazioni proprie o diversi metodi, e i Callable per un breve calcolo. Hai già usato questa idea: la funzione di confronto che passi a sort_custom() è una strategia di ordinamento.
Sfida
MedioUn carrello può cambiare strategia di sconto al momento del pagamento. Discount in discount.gd è la strategia di base: nessuno sconto. Scrivi altre due strategie e il carrello:
PercentOff(creata con una percentuale):apply(total)restituiscetotal - total * percent / 100(aritmetica intera), elabel()restituisce10% offper il 10 percento.FixedOff(creata con un importo):apply(total)restituisce il totale meno l’importo, ma mai un valore inferiore a0, elabel()restituisce30 offper un importo di 30.Cart:set_discount(new_discount)memorizza la strategia echeckout()somma i prezzi e restituisceSubtotal TOTAL, LABEL, pay PRICE, dove PRICE è il valore restituito daapply()della strategia.
Il codice fornito legge i prezzi su una riga, il numero di pagamenti successivi e una strategia per riga: percent N, fixed N oppure none. Per i prezzi 50 30 40 e le strategie percent 10, fixed 30, none, l’output è:
Subtotal 120, 10% off, pay 108
Subtotal 120, 30 off, pay 90
Subtotal 120, no discount, pay 120Il tuo codice va inserito in cart.gd, percent_off.gd e fixed_off.gd. main.gd contiene il codice fornito per l’input/output e non può essere modificato.
Provalo tu
extends Node
func _ready():
# Codice di input/output fornito: lascialo così com'è
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())
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniIntroduzione alla programmazione orientata agli oggettiClassi e oggetti a confrontoLa parola chiave selfMetodiProprietàCostruttore (_init)Riepilogo - Calcolatrice semplice4Polimorfismo
Ripasso dell'override dei metodiDuck typingClassi e metodi astrattiProgettazione delle interfacceRiepilogo - Calcolatore di forme7Segnali
Che cos'è un segnale?Dichiarare ed emettereCollegarsi ai segnaliSegnali con argomentiRipasso - Sistema della salute10Pattern di progettazione, parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern Composite2Proprietà delle classi
Variabili di istanza e staticheFunzioni staticheSetter delle proprietàGetter delle proprietàPrivato per convenzioneRiepilogo - Gestore di conti bancari5Incapsulamento
Membri pubblici e privatiValidazione con i setterNascondimento delle informazioniProprietà di sola letturaRiepilogo - Sistema di gestione dei dati degli studenti8Concetti avanzati di OOP
Composizione vs ereditarietàClassi interneNodi come componentiRefCounted vs NodeEnum nelle classi11Progetto: Gestione della biblioteca
Panoramica del progettoClassi Libro e Utente3Ereditarietà
Ereditarietà di baseLa parola chiave superOverride dei metodiCatene di ereditarietàControlli di tipo con isRipasso - Gerarchia dei dipendenti6Metodi speciali
Introduzione ai metodi virtualiIl metodo _to_stringIteratori personalizzatiRiepilogo - Lista personalizzata9Modelli di progettazione, parte 1
Introduzione ai modelli di progettazionePattern SingletonPattern FactoryPattern ObserverPattern StrategyEsercitati da solo: Compilatore GDScript online