תבנית Observer
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GDScript של Coddy. שיעור 48 מתוך 65.
תבנית 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)
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםמילת המפתח selfמתודותמאפייניםבנאי (_init)חזרה – מחשבון פשוט2מאפייני מחלקה
משתני מופע לעומת משתנים סטטייםפונקציות סטטיותקובעי מאפייניםמאחזרי מאפייניםפרטיים לפי מוסכמהסיכום – מנהל חשבון בנק5כימוס
איברים ציבוריים ופרטייםאימות באמצעות Settersהסתרת מידעמאפיינים לקריאה בלבדסיכום – מערכת לניהול רשומות תלמידיםתרגלו בעצמכם: קומפיילר GDScript אונליין