نمط 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أساسيات البرمجة كائنية التوجه
الملفات الخارجيةمقدمة إلى البرمجة كائنية التوجهالفئات مقابل الكائناتالكلمة المفتاحية selfالأساليبالخصائصالمُنشئ (_init)مراجعة - آلة حاسبة بسيطة4تعدد الأشكال
إعادة النظر في تجاوز الأساليبكتابة الأنواع بأسلوب البطةالفئات والأساليب المجرّدةتصميم الواجهاتمراجعة - حاسبة الأشكال10أنماط التصميم، الجزء الثاني
نمط الأوامرنمط المحوّلنمط المُزيّننمط الطريقة القالبيةنمط الحالةالنمط المركّب2خصائص الأصناف
متغيرات الكائن مقابل المتغيرات الساكنةالدوال الساكنةمُعيِّنات الخصائصمُسترجِعات الخصائصخاص وفقًا للاتفاقيةمراجعة - مدير الحسابات البنكية5التغليف
الأعضاء العامة والخاصةالتحقق من الصحة باستخدام دوال الضبطإخفاء المعلوماتالخصائص للقراءة فقطمراجعة - نظام سجلات الطلاب8مفاهيم متقدمة في البرمجة كائنية التوجه
التركيب مقابل الوراثةالأصناف الداخليةالعُقد كمكوّناتRefCounted مقابل Nodeالتعدادات في الأصنافتدرّب بنفسك: مترجم GDScript عبر الإنترنت