Menu
CoddyTech

전략 패턴

Coddy GDScript 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 65개 중 49번째.

Strategy 패턴은 알고리즘의 각 버전을 별도의 객체에 넣으므로, 프로그램이 실행되는 동안 알고리즘을 바꿔 사용할 수 있습니다. 이를 사용하는 객체인 컨텍스트는 하나의 메서드를 호출하며, 어떤 버전을 가지고 있는지는 신경 쓰지 않습니다.

두 가지 공격 전략, 부모 클래스와 동일한 메서드 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 20

Fighter는 자신이 어떤 스타일을 가지고 있는지 확인하지 않습니다. 새로운 스타일은 새로운 클래스이며, 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

전략에 자체 설정이나 여러 메서드가 있을 때는 클래스를 사용하고, 짧은 계산 하나에는 Callables를 사용하세요. 이 아이디어는 이전에도 사용한 적이 있습니다. sort_custom()에 전달하는 비교 함수는 정렬 전략입니다.

challenge icon

챌린지

중급

상점 장바구니는 결제 시 할인 전략을 바꿀 수 있습니다. discount.gd의 Discount는 기본 전략입니다. 할인은 없습니다. 전략 두 가지와 장바구니를 작성하세요:

  • 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()가 반환하는 값입니다.

제공된 코드는 한 줄에서 가격을 읽고, 이어지는 결제 횟수와 각 줄의 전략을 읽습니다: 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())
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 GDScript 컴파일러