Menu
CoddyTech
flag Ar iconالعربيةdown icon

نمط 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
true

a وb متغيران يشيران إلى الكائن نفسه، لذا يظهر التغيير الذي أُجري عبر a من خلال b. بالنسبة إلى الكائنات، تكون == true فقط عندما يكون الطرفان الكائن نفسه.

لا يستطيع GDScript جعل _init() خاصًا، لذا لا شيء يمنع نصًا برمجيًا من استدعاء GameSettings.new() والحصول على كائن ثانٍ. تُشكّل الشرطة السفلية في _instance والطريقة get_instance() الاتفاقَ: تطلب النصوص البرمجية الكائن، ولا تنشئه أبدًا.

في مشروع Godot، الطريقة المعتادة للحصول على كائن مشترك واحد هي استخدام autoload: وهو نص برمجي تسجّله في إعدادات المشروع، وينشئه Godot مرة واحدة عند بدء اللعبة ويتيحه بالاسم في كل نص برمجي. إنه Singleton يديره Godot نيابةً عنك. تُعدّ Autoloads في المحرر، لذا تبني التحديات هنا هذا النمط باستخدام static var.

challenge icon

التحدي

سهل

تُضاف قيمة كل عملة في اللعبة إلى رصيد مشترك واحد. اجعل 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()))
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجّه

تدرّب بنفسك: مترجم GDScript عبر الإنترنت