نمط Singleton
جزء من قسم البرمجة كائنية التوجّه في رحلة GDScript على Coddy. الدرس 46 من 65.
يضمن نمط Singleton أن يكون للصنف كائن واحد بالضبط، ويمنح كل سكربت طريقة واحدة للوصول إليه. استخدمه للأشياء التي لا يوجد منها في اللعبة سوى نسخة واحدة: الإعدادات، ونظام الحفظ، ومدير النقاط.
في GDScript، يحتفظ static var بالكائن الوحيد، وتنشئ static func هذا الكائن في المرة الأولى التي يطلبه فيها أحد:
# game_settings.gd
class_name GameSettings
extends RefCounted
static var _instance: GameSettings = null
var volume := 50
static func get_instance() -> GameSettings:
if _instance == null:
_instance = GameSettings.new()
print("Creating settings")
return _instanceينتمي _instance إلى الصنف، وليس إلى كائن، لذا يرى كل برنامج نصي المتغير نفسه. يعثر الاستدعاء الأول على أنه فارغ وينشئ الكائن؛ وكل استدعاء لاحق يعيد الكائن نفسه.
# main.gd
var a := GameSettings.get_instance()
var b := GameSettings.get_instance()
a.volume = 80
print(b.volume)
print(a == b)Creating settings
80
truea وb متغيران يشيران إلى الكائن نفسه، لذا يظهر التغيير الذي أُجري عبر a من خلال b. بالنسبة إلى الكائنات، تكون == true فقط عندما يكون الطرفان الكائن نفسه.
لا يستطيع GDScript جعل _init() خاصًا، لذا لا شيء يمنع نصًا برمجيًا من استدعاء GameSettings.new() والحصول على كائن ثانٍ. تُشكّل الشرطة السفلية في _instance والطريقة get_instance() الاتفاقَ: تطلب النصوص البرمجية الكائن، ولا تنشئه أبدًا.
في مشروع Godot، الطريقة المعتادة للحصول على كائن مشترك واحد هي استخدام autoload: وهو نص برمجي تسجّله في إعدادات المشروع، وينشئه Godot مرة واحدة عند بدء اللعبة ويتيحه بالاسم في كل نص برمجي. إنه Singleton يديره Godot نيابةً عنك. تُعدّ Autoloads في المحرر، لذا تبني التحديات هنا هذا النمط باستخدام static var.
التحدي
سهلتُضاف قيمة كل عملة في اللعبة إلى رصيد مشترك واحد. اجعل ScoreManager نمط المفرد:
- يحتفظ
static varبالكائن الوحيد. - تعيد
static func get_instance()هذا الكائن، وتنشئه عند استدعائها لأول مرة. وبعد إنشائه مباشرةً، تطبعScore manager created. - تضيف
add_points(points)نقاطًا إلىscore.
في coin.gd، تضيف collect() قيمة العملة value إلى الرصيد عبر ScoreManager.get_instance().
يقرأ الكود المرفق عدد الأحداث التالية، ثم يقرأ حدثًا واحدًا في كل سطر: يجمع coin VALUE عملة جديدة، ويضيف bonus POINTS نقاطًا مباشرةً. بعد كل حدث، يطبع الرصيد، وفي النهاية يطبع ما إذا كان استدعاءان لـ get_instance() يعيدان الكائن نفسه. عند إدخال coin 5 وbonus 20، يكون الناتج:
Score manager created
Score: 5
Score: 25
Same object: trueضع الكود في score_manager.gd وcoin.gd. يحتوي main.gd على كود الإدخال/الإخراج المرفق، ولا يمكن تعديله.
جرّب بنفسك
extends Node
func _ready():
# كود الإدخال والإخراج المُقدَّم: اتركه كما هو
var count := int(OS.read_string_from_stdin())
for i in count:
var parts := OS.read_string_from_stdin().split(" ")
if parts[0] == "coin":
Coin.new(int(parts[1])).collect()
elif parts[0] == "bonus":
ScoreManager.get_instance().add_points(int(parts[1]))
print("Score: %d" % ScoreManager.get_instance().score)
print("Same object: %s" % (ScoreManager.get_instance() == ScoreManager.get_instance()))
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجّه
1أساسيات البرمجة كائنية التوجه
الملفات الخارجيةمقدمة إلى البرمجة كائنية التوجهالفئات مقابل الكائناتالكلمة المفتاحية selfالأساليبالخصائصالمُنشئ (_init)مراجعة - آلة حاسبة بسيطة4تعدد الأشكال
إعادة النظر في تجاوز الأساليبكتابة الأنواع بأسلوب البطةالفئات والأساليب المجرّدةتصميم الواجهاتمراجعة - حاسبة الأشكال10أنماط التصميم، الجزء الثاني
نمط الأوامرنمط المحوّلنمط المُزيّننمط الطريقة القالبيةنمط الحالةالنمط المركّب2خصائص الأصناف
متغيرات الكائن مقابل المتغيرات الساكنةالدوال الساكنةمُعيِّنات الخصائصمُسترجِعات الخصائصخاص وفقًا للاتفاقيةمراجعة - مدير الحسابات البنكية5التغليف
الأعضاء العامة والخاصةالتحقق من الصحة باستخدام دوال الضبطإخفاء المعلوماتالخصائص للقراءة فقطمراجعة - نظام سجلات الطلاب8مفاهيم متقدمة في البرمجة كائنية التوجه
التركيب مقابل الوراثةالأصناف الداخليةالعُقد كمكوّناتRefCounted مقابل Nodeالتعدادات في الأصنافتدرّب بنفسك: مترجم GDScript عبر الإنترنت