옵저버 패턴
Coddy GDScript 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 65개 중 48번째.
Observer 패턴을 사용하면 한 객체인 주체가 다른 여러 객체인 관찰자에게 어떤 일이 일어났다고 알릴 수 있습니다. 주체는 관찰자들이 그 소식으로 무엇을 하는지 알지 못하며, 그저 알리기만 합니다.
직접 작성하면 subject가 observer 배열을 유지하고 각 observer에서 동일한 메서드를 호출합니다:
# health.gd
class_name Health
extends RefCounted
var observers: Array = []
var hp := 100
func add_observer(observer) -> void:
observers.append(observer)
func take_damage(amount: int) -> void:
hp -= amount
for observer in observers:
observer.on_hp_changed(hp)Godot에는 이 패턴이 기본으로 제공됩니다. 시그널은 주체의 알림이며, connect()를 호출할 때마다 관찰자가 추가되므로 배열과 반복문은 더 이상 필요하지 않습니다.
# health.gd
class_name Health
extends RefCounted
signal hp_changed(new_hp: int)
var hp := 100
func take_damage(amount: int) -> void:
hp -= amount
hp_changed.emit(hp)옵저버는 매개변수가 시그널과 일치하는 메서드를 가진 객체입니다:
# health_bar.gd
class_name HealthBar
extends RefCounted
func on_hp_changed(new_hp: int) -> void:
print("Bar: ", new_hp, "/100")# damage_log.gd
class_name DamageLog
extends RefCounted
func on_hp_changed(new_hp: int) -> void:
if new_hp < 50:
print("Log: low health!")# main.gd
var health := Health.new()
var bar := HealthBar.new()
var damage_log := DamageLog.new()
health.hp_changed.connect(bar.on_hp_changed)
health.hp_changed.connect(damage_log.on_hp_changed)
health.take_damage(70)Bar: 30/100
Log: low health!Health는 막대나 로그가 존재하는지 전혀 모릅니다. 사운드 효과나 업적도 health.gd를 전혀 변경하지 않고 이벤트를 수신할 수 있습니다.
관찰자가 제대로 작동하도록 하는 세 가지 규칙:
- 같은 메서드를 두 번 연결하면 오류가 발생합니다. 그런 일이 발생할 수 있다면 먼저
hp_changed.is_connected(bar.on_hp_changed)로 확인하세요.disconnect()는 관찰자를 제거하며, 연결되어 있지 않은 관찰자의 연결을 해제하는 것도 오류입니다. - 연결만으로는 관찰자가 계속 살아 있지 않습니다. 관찰자를 변수나 배열에 보관하세요.
connect()호출 안에서 생성한 객체는 즉시 해제되어 신호를 받을 수 없습니다. - 관찰자의 메서드를 연결하세요. 주체를 사용하는 람다를 주체 자신의 신호에 연결하면 주체가 영원히 살아 있게 되며, Godot은 게임을 종료할 때 이를 누수로 보고합니다.
챌린지
중급시그널로 디스플레이에 알림을 보내는 기상 관측소를 만드세요.
weather_station.gd에서:
- 시그널
temperature_changed(value: float)을 선언하세요. attach(display)는 디스플레이의on_temperature_changed메서드를 연결합니다. 이미 연결된 디스플레이를 연결해도 아무 작업도 하지 않습니다.detach(display)는 연결을 해제합니다. 연결되지 않은 디스플레이의 연결을 해제해도 아무 작업도 하지 않습니다.set_temperature(value)는 값을temperature에 저장하고 해당 값과 함께 시그널을 발생시킵니다.
weather_display.gd에서 on_temperature_changed(value)는 디스플레이 이름과 값을 소수점 한 자리까지 출력합니다. 예: Phone: 21.5 C.
제공된 코드는 한 줄에 디스플레이 이름을 읽고, 이어지는 명령의 개수를 읽은 다음, 한 줄에 하나씩 명령을 읽습니다: attach NAME, detach NAME 또는 temp VALUE. 마지막에는 최종 온도를 출력합니다. 이름이 Phone Tablet이고 명령이 attach Phone, attach Tablet, temp 21.5, detach Phone, temp 19일 때 출력은 다음과 같습니다:
Phone: 21.5 C
Tablet: 21.5 C
Tablet: 19.0 C
Final temperature: 19.0코드는 weather_station.gd와 weather_display.gd에 작성하세요. main.gd에는 제공된 입출력 코드가 있으며 수정할 수 없습니다.
직접 해보기
extends Node
func _ready():
# 제공된 입출력 코드: 그대로 유지하세요
var station := WeatherStation.new()
var displays := {}
for display_name in OS.read_string_from_stdin().split(" "):
displays[display_name] = WeatherDisplay.new(display_name)
var count := int(OS.read_string_from_stdin())
for i in count:
var parts := OS.read_string_from_stdin().split(" ")
match parts[0]:
"attach":
station.attach(displays[parts[1]])
"detach":
station.detach(displays[parts[1]])
"temp":
station.set_temperature(float(parts[1]))
print("Final temperature: %.1f" % station.temperature)
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: 온라인 GDScript 컴파일러