Menu
CoddyTech

Observer Deseni

Coddy'nin GDScript Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 48 / 65.

Observer kalıbı, özne olan bir nesnenin birçok başka nesneye, yani gözlemcilere, bir şeyin gerçekleştiğini bildirmesini sağlar. Özne, gözlemcilerin bu bilgiyle ne yaptığını bilmez; yalnızca duyurur.

Elle yazıldığında, konu bir gözlemci dizisi tutar ve her birinde aynı yöntemi çağırır:

# 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'ta bu desen yerleşik olarak bulunur. Bir signal, öznenin duyurusudur ve her connect() bir gözlemci ekler; böylece diziye ve döngüye artık gerek kalmaz:

# 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)

Gözlemci, parametreleri sinyalle eşleşen bir yönteme sahip herhangi bir nesnedir:

# 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 bir çubuğun ya da günlüğün varlığından haberdar değildir. health.gd dosyasında tek bir değişiklik yapmadan bir ses efekti veya başarı da dinleyebilir.

Gözlemcilerin çalışmasını sağlayan üç kural vardır:

  • Aynı yöntemi iki kez bağlamak hatadır. Bu durumun yaşanabileceği yerde, önce hp_changed.is_connected(bar.on_hp_changed) ile kontrol et. disconnect() bir gözlemciyi kaldırır ve bağlı olmayan bir gözlemcinin bağlantısını kesmek de hatadır.
  • Bir bağlantı, gözlemcinin bellekte kalmasını sağlamaz. Gözlemciyi bir değişkende veya dizide tut: connect() çağrısının içinde oluşturulan bir nesne hemen bellekten silinir ve sinyali hiçbir zaman duymaz.
  • Gözlemcilerin yöntemlerini bağla. Konuyu kullanan ve konunun kendi sinyaline bağlanan bir lambda, konunun sonsuza dek bellekte kalmasına neden olur; Godot bunu oyun kapandığında bellek sızıntısı olarak bildirir.
challenge icon

Görev

Orta

Bir sinyalle ekranlarına bildirim gönderen bir hava istasyonu oluşturun.

weather_station.gd dosyasında:

  • temperature_changed(value: float) sinyalini tanımlayın.
  • attach(display), ekranın on_temperature_changed yöntemine bağlanır. Zaten bağlı olan bir ekranı eklemek hiçbir şey yapmaz.
  • detach(display) ekranın bağlantısını keser. Bağlı olmayan bir ekranın bağlantısını kesmek hiçbir şey yapmaz.
  • set_temperature(value), değeri temperature içine kaydeder ve sinyali bu değerle yayar.

weather_display.gd dosyasında on_temperature_changed(value), ekran adını ve değeri bir ondalık basamakla yazdırır; örneğin Phone: 21.5 C.

Sağlanan kod, ekran adlarını tek satırda, ardından kaç komut geleceğini ve her satırda bir komut okuyup işler: attach NAME, detach NAME veya temp VALUE. Sonunda son sıcaklığı yazdırır. Phone Tablet adları ve attach Phone, attach Tablet, temp 21.5, detach Phone, temp 19 komutları için çıktı şöyledir:

Phone: 21.5 C
Tablet: 21.5 C
Tablet: 19.0 C
Final temperature: 19.0

Kodunuzu weather_station.gd ve weather_display.gd dosyalarına yazın. main.gd, sağlanan giriş/çıkış kodunu içerir ve düzenlenemez.

Kendin dene

extends Node

func _ready():
	# Sağlanan girdi/çıktı kodu: olduğu gibi bırakın
	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)
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler

Kendi başına pratik yap: Online GDScript derleyicisi