Patron Strategy
Fait partie de la section Programmation orientée objet du Journey GDScript de Coddy. Leçon 49 sur 65.
Le patron Strategy place chaque version d’un algorithme dans son propre objet, afin que tu puisses remplacer l’algorithme pendant l’exécution du programme. L’objet qui l’utilise, le contexte, appelle une méthode et ne se soucie pas de la version qu’il contient.
Voici deux stratégies d’attaque, une classe parent et une sous-classe avec la même méthode 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 * 2Le contexte stocke une stratégie et lui délègue le calcul :
# 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 ne vérifie jamais quel style il possède. Un nouveau style correspond à une nouvelle classe, et fighter.gd reste tel quel ; une chaîne if style == "heavy" dans attack() s’allongerait avec chaque style.
Lorsqu’une stratégie est une petite fonction sans données qui lui sont propres, un Callable suffit. Le contexte stocke la fonction et l’exécute avec 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
55Utilisez des classes lorsqu’une stratégie a ses propres paramètres ou plusieurs méthodes, et des Callables pour un calcul court. Vous avez déjà utilisé cette idée : la fonction de comparaison que vous transmettez à sort_custom() est une stratégie de tri.
Défi
MoyenUn panier peut changer de stratégie de remise au moment du paiement. Discount dans discount.gd est la stratégie de base : aucune remise. Écris-en deux autres ainsi que le panier :
PercentOff(créée avec un pourcentage) :apply(total)renvoietotal - total * percent / 100(arithmétique entière), etlabel()renvoie10% offpour 10 pour cent.FixedOff(créée avec un montant) :apply(total)renvoie le total moins le montant, sans jamais descendre sous0, etlabel()renvoie30 offpour un montant de 30.Cart:set_discount(new_discount)enregistre la stratégie, etcheckout()additionne les prix et renvoieSubtotal TOTAL, LABEL, pay PRICE, où PRICE est la valeur renvoyée parapply()de la stratégie.
Le code fourni lit les prix sur une ligne, le nombre de paiements qui suivent, puis une stratégie par ligne : percent N, fixed N ou none. Pour les prix 50 30 40 et les stratégies percent 10, fixed 30, none, la sortie est :
Subtotal 120, 10% off, pay 108
Subtotal 120, 30 off, pay 90
Subtotal 120, no discount, pay 120Ton code va dans cart.gd, percent_off.gd et fixed_off.gd. main.gd contient le code fourni pour les entrées/sorties et ne peut pas être modifié.
Essayez vous-même
extends Node
func _ready():
# Code d'entrée/sortie fourni : conservez-le tel quel
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())
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation orientée objet
1Fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses et objetsLe mot-clé selfMéthodesPropriétésConstructeur (_init)Récapitulatif - Calculatrice simple4Polymorphisme
Retour sur la redéfinition des méthodesTypage canardClasses et méthodes abstraitesConception d’interfacesRécapitulatif – Calculateur de formes7Signaux
Qu’est-ce qu’un signal ?Déclarer et émettreSe connecter aux signauxSignaux avec argumentsRécapitulatif - système de santé10Patrons de conception, partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode modèlePatron ÉtatPatron Composite2Propriétés de classe
Variables d’instance et variables statiquesFonctions statiquesSetters de propriétésGetters de propriétésPrivé par conventionRécapitulatif - Gestionnaire de comptes bancaires5Encapsulation
Membres publics et privésValidation avec les méthodes setterMasquage de l’informationPropriétés en lecture seuleRécapitulatif - Système de dossiers étudiants8Concepts avancés de la POO
Composition ou héritageClasses internesLes nœuds comme composantsRefCounted ou NodeEnums dans les classes3Héritage
Héritage de baseLe mot-clé superRedéfinition de méthodesChaînes d’héritageVérifier les types avec isRécapitulatif – Hiérarchie des employés6Méthodes spéciales
Introduction aux méthodes virtuellesLa méthode _to_stringItérateurs personnalisésRécapitulatif – Liste personnalisée9Patrons de conception, partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FactoryPatron ObserverPatron StrategyEntraînez-vous par vous-même : Compilateur GDScript en ligne