ストラテジーパターン
CoddyのGDScriptジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 49/65。
Strategyパターンでは、アルゴリズムの各バージョンをそれぞれ独自のオブジェクトに格納するため、プログラムの実行中にアルゴリズムを切り替えられます。それを使用するオブジェクトであるコンテキストは、1つのメソッドを呼び出すだけで、どのバージョンを保持しているかを気にしません。
ここでは、2つの攻撃戦略、親クラスと同じメソッド 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コンテキストは戦略を保持し、計算をその戦略に委ねます:
# 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は、自分がどのスタイルを持っているかを確認しません。新しいスタイルは新しいクラスであり、fighter.gdはそのままです。attack()内のif style == "heavy"の連鎖は、スタイルが増えるたびに長くなっていきます。
戦略が独自のデータを持たない小さな関数ひとつであれば、Callableで十分です。コンテキストに関数を保存し、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戦略に独自の設定や複数のメソッドがある場合はクラスを使い、短い計算を1つ行う場合はCallableを使います。この考え方は以前にも使いました。sort_custom()に渡す比較関数は、ソート戦略です。
チャレンジ
中級チェックアウト時に、ショップのカートは割引戦略を切り替えられます。discount.gd の Discount は基底戦略で、割引はありません。さらに2つの戦略とカートを作成してください。
PercentOff(パーセントを指定して作成):apply(total)はtotal - total * percent / 100を返します(整数演算)。また、10パーセントの場合、label()は10% offを返します。FixedOff(金額を指定して作成):apply(total)は合計から金額を差し引いた値を返しますが、0未満にはなりません。また、金額が30の場合、label()は30 offを返します。Cart:set_discount(new_discount)は戦略を保存し、checkout()は価格を合計してSubtotal TOTAL, LABEL, pay PRICEを返します。ここで PRICE は、戦略のapply()が返す値です。
提供されているコードは、1行目の価格、続くチェックアウトの回数、そして各行に1つずつ戦略を読み取ります。戦略は percent N、fixed N、または none です。価格が 50 30 40 で、戦略が percent 10、fixed 30、none の場合、出力は次のとおりです。
Subtotal 120, 10% off, pay 108
Subtotal 120, 30 off, pay 90
Subtotal 120, no discount, pay 120コードは cart.gd、percent_off.gd、fixed_off.gd に記述してください。main.gd には提供された入出力コードが含まれており、編集できません。
自分で試してみよう
extends Node
func _ready():
# 提供された入出力コード:そのままにしてください
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())
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: GDScriptオンラインコンパイラ