Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

جدول Singleton

جزء من قسم البرمجة كائنية التوجه في رحلة Lua على Coddy. الدرس 63 من 70.

أحيانًا تحتاج إلى نسخة واحدة بالضبط من شيء ما في برنامجك بأكمله: مدير إعدادات، أو نظام تسجيل، أو إعدادات اللعبة. قد يتسبب إنشاء نسخ متعددة في حدوث مشكلات: أيٌّ منها يحتوي على البيانات الصحيحة؟ يضمن نمط Singleton وجود نسخة واحدة فقط.

في Lua، يُعدّ تنفيذ Singleton أمرًا بسيطًا بشكل مدهش بفضل طريقة عمل الوحدات. عند استخدام require لوحدة، تخزّن Lua النتيجة مؤقتًا. وتُعيد الاستدعاءات اللاحقة لـ require الجدول المخزّن مؤقتًا، وليس جدولًا جديدًا.

-- GameConfig.lua
local GameConfig = {
    volume = 80,
    difficulty = "normal"
}

function GameConfig:setVolume(v)
    self.volume = v
end

return GameConfig

الآن، في أي مكان داخل برنامجك:

local config1 = require("GameConfig")
local config2 = require("GameConfig")

config1:setVolume(50)
print(config2.volume)  -- 50 (نفس الجدول!)

يشير كلٌّ من config1 وconfig2 إلى الجدول نفسه تمامًا. وتظهر التغييرات التي تُجرى من خلال أحد المتغيرين من خلال الآخر. يحدث هذا تلقائيًا لأن require في Lua يخزّن القيمة المُعادة في package.loaded ويعيد استخدامها.

على عكس نمط Factory الذي ينشئ كائنات جديدة، يضمن Singleton حالةً مشتركة عبر تطبيقك بالكامل. وهذا مثالي للإعدادات العامة التي تحتاج أجزاء متعددة من التعليمات البرمجية لديك إلى الوصول إليها وتعديلها باستمرار.

challenge icon

التحدي

سهل

لننشئ مديرًا لإعدادات اللعبة باستخدام نمط Singleton! ستنشئ وحدة تهيئة تضمن وصول كل جزء من برنامجك إلى الإعدادات نفسها تمامًا، بغض النظر عن عدد مرات طلبها.

ستنظم التعليمات البرمجية عبر ملفين:

  • GameSettings.lua: أنشئ وحدة Singleton تخزّن تهيئة اللعبة. يجب أن يتضمن جدول الإعدادات لديك musicVolume (يبدأ بقيمة 70)، وsfxVolume (يبدأ بقيمة 100)، وdifficulty (يبدأ بقيمة "normal"). أضف ثلاث طرق: :setMusicVolume(value)، و:setSfxVolume(value)، و:setDifficulty(level) لتعديل هذه الإعدادات. تذكّر إعادة الجدول في نهاية الوحدة!
  • main.lua: أثبت أن Singleton يعمل من خلال طلب وحدة GameSettings مرتين داخل متغيرين مختلفين. استخدم المتغير الأول لتغيير مستوى صوت الموسيقى ودرجة الصعوبة. ثم استخدم المتغير الثاني لطباعة الإعدادات الثلاثة. إذا كان Singleton يعمل بشكل صحيح، فسيشير كلا المتغيرين إلى الجدول نفسه، ولذلك ستكون التغييرات التي تُجرى من خلال أحدهما مرئية من خلال الآخر.

ستتلقى مُدخلين:

  1. مستوى صوت الموسيقى الجديد (رقم)
  2. مستوى الصعوبة الجديد (سلسلة نصية مثل "easy" أو "hard")

بعد تعديل الإعدادات من خلال المتغير الأول، اطبع الإعدادات الثلاثة باستخدام المتغير الثاني، كل إعداد في سطر مستقل وبهذا الترتيب: مستوى صوت الموسيقى، ثم مستوى صوت المؤثرات الصوتية، ثم درجة الصعوبة.

على سبيل المثال، إذا كانت المدخلات هي 50 وhard، فيجب أن يكون الناتج:

50
100
hard

إذا كانت المدخلات هي 25 وeasy، فيجب أن يكون الناتج:

25
100
easy

لاحظ أن مستوى صوت المؤثرات الصوتية يظل بقيمته الافتراضية 100 لأننا لم نغيّره مطلقًا، بينما يعكس مستوى صوت الموسيقى ودرجة الصعوبة التغييرات التي أُجريت من خلال المتغير الأول، مما يثبت أن كلا المتغيرين يشيران إلى مثيل Singleton نفسه!

جرّب بنفسك

-- main.lua - توضيح نمط الـ Singleton

-- قراءة المدخلات
local newMusicVolume = tonumber(io.read())
local newDifficulty = io.read()

-- TODO: استيراد وحدة GameSettings إلى متغير (مثل settings1)

-- TODO: استيراد وحدة GameSettings مرة أخرى إلى متغير مختلف (مثل settings2)
-- (إذا عمل نمط Singleton بشكل صحيح، فإن كلا المتغيرين يشيران إلى نفس الجدول!)

-- TODO: استخدم المتغير الأول لـ:
--   - تعيين مستوى صوت الموسيقى إلى newMusicVolume
--   - تعيين الصعوبة إلى newDifficulty

-- TODO: استخدم المتغير الثاني لطباعة الإعدادات
-- اطبع كلًا منها في سطره الخاص: musicVolume, sfxVolume, difficulty
quiz iconاختبر نفسك

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

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

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