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

الوصول عبر Closures

جزء من قسم Object Oriented Programming في رحلة Lua على Coddy — الدرس 45 من 70.

المتغيرات المحلية الخاصة عديمة الفائدة إذا لم تتمكن أبدًا من التفاعل معها. الحل هو تعريف دوال الجلب (getter) والتعيين (setter) داخل المنشئ (constructor)—حيث يكون لديها صلاحية الوصول إلى نطاق الإغلاق (closure's scope).

بما أن هذه الدوال يتم إنشاؤها داخل :new()، فبإمكانها "رؤية" المتغيرات المحلية. أنت تقوم بتعيينها مباشرة للكائن:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- متغير خاص
    
    local obj = {}
    
    -- تعريف الدوال داخل المنشئ
    function obj:getCount()
        return count
    end
    
    function obj:increment()
        count = count + 1
    end
    
    setmetatable(obj, Counter)
    return obj
end

الآن يمكن الوصول إلى المتغير الخاص count—ولكن فقط من خلال الأساليب التي توفرها:

local c = Counter:new()
print(c:getCount())  -- 0
c:increment()
c:increment()
print(c:getCount())  -- 2
print(c.count)       -- nil (لا يزال مخفياً!)

لاحظ أن :getCount() و :increment() مُعرفة على obj مباشرة، وليس على جدول الفئة (class table). تحصل كل نسخة (instance) على نسختها الخاصة من هذه الدوال، وكل منها لديه وصول إلى الـ count الخاص بها. هذه هي المقايضة: الخصوصية الحقيقية تكلف ذاكرة إضافية قليلاً لأن الطرق (methods) لا يتم مشاركتها عبر النموذج الأولي (prototype).

challenge icon

التحدي

سهل

لنقم ببناء فئة Temperature تستخدم الخصوصية القائمة على الإغلاق (closure-based privacy) لتخزين قيمة درجة الحرارة بالدرجة المئوية (Celsius)، مع تحديد دوال الجلب (getter) والتعيين (setter) داخل المنشئ (constructor) لتوفير وصول محكم!

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

  • Temperature.lua: قم بإنشاء فئة حيث يتم تخزين قيمة درجة الحرارة في متغير محلي داخل المنشئ—مخفي تماماً عن الوصول الخارجي. يجب أن يقوم المنشئ :new(initialCelsius) بتخزين درجة الحرارة في متغير محلي، ثم تحديد هذه الدوال مباشرة على مثيل الكائن (object instance):
    • :getCelsius() — يعيد درجة الحرارة الحالية بالدرجة المئوية.
    • :setCelsius(value) — يقوم بتحديث درجة الحرارة إلى قيمة جديدة.
    • :getFahrenheit() — يعيد درجة الحرارة محولة إلى فهرنهايت باستخدام الصيغة: (celsius * 9/5) + 32
  • main.lua: قم باستدعاء وحدة Temperature الخاصة بك واقرأ قيمتين من الإدخال: درجة حرارة أولية ودرجة حرارة جديدة لتعيينها. أنشئ مثيلاً من Temperature بالقيمة الأولية، واطبع قراءات Celsius و Fahrenheit، ثم استخدم دالة التعيين لتغيير درجة الحرارة واطبع كلتا القراءتين مرة أخرى.

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

  1. درجة الحرارة الأولية بالدرجة المئوية (رقم)
  2. درجة حرارة جديدة لتعيينها (رقم)

يجب أن يتكون مخرجك من أربعة أسطر تعرض قراءات درجة الحرارة قبل وبعد التغيير:

Celsius: {initialValue}
Fahrenheit: {convertedInitial}
Celsius: {newValue}
Fahrenheit: {convertedNew}

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

Celsius: 0
Fahrenheit: 32
Celsius: 100
Fahrenheit: 212

تذكر: يجب تخزين قيمة درجة الحرارة في متغير محلي داخل :new()، وليس على self. يتم تعريف دوال الجلب والتعيين داخل المنشئ حيث يمكنها الوصول إلى هذا المتغير الخاص من خلال الإغلاق (closure). محاولة الوصول إلى temp.celsius أو temp._celsius مباشرة يجب أن تعيد nil!

ورقة مرجعية

لإنشاء متغيرات خاصة (private) حقاً في فئات Lua، قم بتعريف دوال الجلب (getter) والتعيين (setter) داخل المنشئ (constructor) حيث يكون لديها وصول إلى المتغيرات المحلية من خلال الإغلاق (closure).

يتم تعيين الدوال مباشرة لمثيل الكائن (وليس لجدول الفئة)، مما يمنح كل مثيل نسخته الخاصة مع إمكانية الوصول إلى متغيراته الخاصة:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- متغير خاص
    
    local obj = {}
    
    -- تعريف الدوال داخل المنشئ
    function obj:getCount()
        return count
    end
    
    function obj:increment()
        count = count + 1
    end
    
    setmetatable(obj, Counter)
    return obj
end

الاستخدام:

local c = Counter:new()
print(c:getCount())  -- 0
c:increment()
c:increment()
print(c:getCount())  -- 2
print(c.count)       -- nil (لا يزال مخفياً!)

المقايضة: يحصل كل مثيل على نسخته الخاصة من الدوال (لا يتم مشاركتها عبر النموذج الأولي)، مما يستهلك ذاكرة أكبر ولكنه يوفر خصوصية حقيقية.

جرّب بنفسك

-- استيراد وحدة Temperature
local Temperature = require('Temperature')

-- قراءة قيم المدخلات
local initialTemp = tonumber(io.read())
local newTemp = tonumber(io.read())

-- TODO: إنشاء مثيل (instance) لدرجة الحرارة بالقيمة الأولية

-- TODO: طباعة قراءات السيلزيوس والفهرنهايت الأولية

-- TODO: استخدام دالة التعيين (setter) لتغيير درجة الحرارة إلى newTemp

-- TODO: طباعة قراءات السيلزيوس والفهرنهايت المحدثة
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming