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

استخدام Closures للخصوصية

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

تشير اتفاقيات التسمية إلى القصد، لكنها لا تمنع الوصول فعليًا. لا يزال بإمكان أي شخص كتابة player._health = 999 وسيعمل ذلك. من أجل خصوصية حقيقية، تقدم Lua نهجًا مختلفًا: تخزين البيانات في متغيرات محلية داخل المنشئ بدلاً من self.

الفكرة الأساسية هي أن المتغيرات المحلية داخل دالة ما لا يمكن الوصول إليها إلا داخل تلك الدالة—وأي دوال معرفة بداخلها. يسمى هذا closure:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- خاص تماماً! ليس ضمن self.
    
    local obj = {}
    setmetatable(obj, Counter)
    return obj
end

في هذا المثال، count موجود فقط داخل الدالة :new(). إنه غير مخزن في obj، لذا لا توجد طريقة للوصول إليه من الخارج—myCounter.count يعيد nil، ولا يوجد حقل مسبوق بشرطة سفلية لتجاوزه.

المتغير count يعيش في نطاق الإغلاق (closure). يظل مخفياً تماماً عن الكود الخارجي. (كما هو مكتوب، لا يوجد شيء يلتقط count بعد؛ في الدرس القادم ستضيف طرقاً (methods) داخل المنشئ (constructor) تغلق عليه، بحيث يستمر طالما بقي الكائن موجوداً.) على عكس self._count، الذي هو مجرد اقتراح مهذب، فإن هذا النهج يجعل البيانات غير قابلة للوصول إليها حقاً.

بالطبع، البيانات المخفية ليست مفيدة إذا لم تتمكن أبداً من التفاعل معها. في الدرس القادم، ستتعلم كيفية تعريف الأساليب (methods) داخل المنشئ (constructor) التي يمكنها قراءة وتعديل هذه المتغيرات الخاصة (private variables) - مما يمنحك وصولاً متحكماً فيه مع الحفاظ على التغليف (encapsulation) الحقيقي.

challenge icon

التحدي

سهل

لنقم ببناء فئة SecretKeeper توضح خصوصية البيانات الحقيقية باستخدام الإغلاقات (closures)! على عكس اتفاقية الشرطة السفلية (underscore) من الدرس السابق، فإن هذا النهج يجعل البيانات غير قابلة للوصول إليها حقاً من خارج الكائن.

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

  • SecretKeeper.lua: قم بإنشاء فئة حيث يتم تخزين السر في متغير محلي (local variable) داخل المنشئ (constructor) — وليس في self. يجب أن يقوم المنشئ :new(secretValue) بتخزين السر في متغير محلي موجود فقط داخل الإغلاق. في الوقت الحالي، لن يكون لدى الكائن أي طريقة للوصول إلى هذا السر (سيأتي ذلك في الدرس القادم!)، ولكن يجب عليك أيضاً تخزين حقل عام name مضبوط على "Keeper" في المثيل (instance) حتى نتمكن من التحقق من عمل الكائن.
  • main.lua: قم باستدعاء وحدة SecretKeeper الخاصة بك واقرأ قيمة سرية من المدخلات. قم بإنشاء مثيل SecretKeeper بهذا السر. ثم أثبت أن السر خاص حقاً عن طريق طباعة ثلاثة أشياء:
    • حقل name الخاص بـ keeper (يجب أن يطبع Keeper)
    • ماذا يحدث عندما تحاول الوصول إلى keeper.secret (يجب أن يكون nil)
    • ماذا يحدث عندما تحاول الوصول إلى keeper._secret (يجب أن يكون أيضاً nil)

ستتلقى مدخلاً واحداً:

  1. القيمة السرية المراد تخزينها (على سبيل المثال، TopSecret123)

يجب أن يكون مخرجك عبارة عن ثلاثة أسطر توضح أنه بينما يعمل الحقل العام، فإن السر مخفي تماماً:

Name: Keeper
secret: nil
_secret: nil

سيكون المخرج هو نفسه بغض النظر عن السر الذي تمرره — لأنه لا توجد طريقة للوصول إليه! المتغير المحلي داخل المنشئ غير مرئي للعالم الخارجي. هذا يثبت أن الخصوصية القائمة على الإغلاق تختلف جوهرياً عن اتفاقية تسمية الشرطة السفلية.

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

Name: Keeper
secret: nil
_secret: nil

جرّب بنفسك

-- استدعاء وحدة SecretKeeper
local SecretKeeper = require('SecretKeeper')

-- قراءة القيمة السرية من المدخلات
local secretValue = io.read()

-- TODO: إنشاء مثيل من SecretKeeper بالقيمة السرية

-- TODO: طباعة حقل الاسم الخاص بـ keeper (التنسيق: "Name: <name>")

-- TODO: طباعة ما يحدث عند الوصول إلى keeper.secret (التنسيق: "secret: <value>")

-- TODO: طباعة ما يحدث عند الوصول إلى keeper._secret (التنسيق: "_secret: <value>")
quiz iconاختبر نفسك

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

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