Menu
CoddyTech

Pola Observer

Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan GDScript di Coddy. Pelajaran 48 dari 65.

Pola Observer memungkinkan satu object, yaitu subject, memberi tahu banyak object lain, yaitu observer, bahwa sesuatu telah terjadi. Subject tidak mengetahui apa yang dilakukan observer dengan informasi tersebut; subject hanya mengumumkannya.

Jika ditulis secara manual, subjek menyimpan array observer dan memanggil method yang sama pada setiap 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 sudah memiliki pola ini. Sinyal adalah pengumuman dari subjek, dan setiap connect() menambahkan pengamat, sehingga array dan perulangan tidak lagi diperlukan:

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

Observer adalah object apa pun yang memiliki method dengan parameter yang cocok dengan sinyal:

# 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 tidak tahu bahwa bar atau log ada. Efek suara atau pencapaian juga bisa mendengarkan, tanpa mengubah health.gd sedikit pun.

Tiga aturan menjaga pengamat tetap berfungsi:

  • Menghubungkan method yang sama dua kali akan menyebabkan error. Jika hal itu mungkin terjadi, periksa terlebih dahulu dengan hp_changed.is_connected(bar.on_hp_changed). disconnect() menghapus pengamat, dan memutuskan koneksi yang belum terhubung juga menyebabkan error.
  • Koneksi tidak membuat pengamat tetap hidup. Simpan pengamat dalam variabel atau array: object yang dibuat di dalam pemanggilan connect() langsung dibebaskan dan tidak akan pernah menerima sinyal.
  • Hubungkan method milik pengamat. Lambda yang menggunakan subjek dan terhubung ke sinyal milik subjek sendiri akan membuat subjek tetap hidup selamanya, yang dilaporkan Godot sebagai kebocoran saat game ditutup.
challenge icon

Tantangan

Sedang

Buat stasiun cuaca yang memberi tahu display-nya dengan sinyal.

Di weather_station.gd:

  • Deklarasikan sinyal temperature_changed(value: float).
  • attach(display) menghubungkan display ke method on_temperature_changed. Memasang display yang sudah terpasang tidak melakukan apa pun.
  • detach(display) memutuskan hubungannya. Melepas display yang belum terpasang tidak melakukan apa pun.
  • set_temperature(value) menyimpan nilai di temperature dan memancarkan sinyal dengan nilai tersebut.

Di weather_display.gd, on_temperature_changed(value) mencetak nama display dan nilainya dengan satu angka desimal, seperti Phone: 21.5 C.

Kode yang disediakan membaca nama display dalam satu baris, jumlah perintah berikutnya, lalu satu perintah per baris: attach NAME, detach NAME, atau temp VALUE. Pada akhirnya, kode mencetak suhu terakhir. Untuk nama Phone Tablet dan perintah attach Phone, attach Tablet, temp 21.5, detach Phone, temp 19, output-nya adalah:

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

Tulis kodemu di weather_station.gd dan weather_display.gd. main.gd berisi kode input/output yang disediakan dan tidak dapat diedit.

Coba sendiri

extends Node

func _ready():
	# Kode input/output yang disediakan: biarkan apa adanya
	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 iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Pemrograman Berorientasi Objek

Latihan sendiri: Compiler GDScript online