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

جدول Singleton

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

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

في لغة Lua، يعد تنفيذ نمط Singleton بسيطاً بشكل مدهش بفضل طريقة عمل الوحدات (modules). فعندما تستخدم 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! ستقوم بإنشاء وحدة تكوين (configuration module) تضمن أن كل جزء من برنامجك يصل إلى نفس الإعدادات تماماً—بغض النظر عن عدد مرات استدعائها.

ستقوم بتنظيم الكود الخاص بك عبر ملفين:

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

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

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

بعد تعديل الإعدادات من خلال المتغير الأول، اطبع الإعدادات الثلاثة باستخدام المتغير الثاني، كل منها في سطر مستقل بهذا الترتيب: music volume، ثم sfx volume، ثم difficulty.

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

50
100
hard

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

25
100
easy

لاحظ أن مستوى صوت المؤثرات الصوتية (sfx volume) يظل عند قيمته الافتراضية 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اختبر نفسك

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

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