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

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

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

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

الفكرة الأساسية هي أن المتغيرات local داخل function لا يمكن الوصول إليها إلا ضمن تلك function، وأي function يتم تعريفها داخلها. يُسمّى هذا 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 بعد؛ في الدرس التالي ستضيف أساليب داخل المُنشئ تُغلِق عليه، ولذلك يستمر طوال فترة بقاء الكائن.) وعلى خلاف self._count، الذي ليس سوى اقتراح مهذب، فإن هذا الأسلوب يجعل data غير قابلة للوصول إليها فعلًا.

بالطبع، لن تكون data المخفية مفيدة إذا لم تتمكن من التفاعل معها مطلقًا. في الدرس التالي، ستتعلم كيفية تعريف أساليب داخل مُنشئ الكائن يمكنها قراءة هذه المتغيرات private وتعديلها، مما يمنحك وصولًا مضبوطًا مع الحفاظ على التغليف الحقيقي.

challenge icon

التحدي

سهل

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

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

  • SecretKeeper.lua: أنشئ فئة تُخزَّن فيها القيمة السرية في متغير محلي داخل المُنشئ، وليس على self. يجب أن يخزّن المُنشئ :new(secretValue) السر في متغير محلي لا يوجد إلا داخل الإغلاق. في الوقت الحالي، لن تكون لدى الكائن أي وسيلة للوصول إلى هذا السر (سيأتي ذلك في الدرس التالي!)، ولكن يجب عليك أيضًا تخزين حقل عام name مضبوطًا على "Keeper" في الكائن حتى نتمكن من التحقق من أن الكائن يعمل.
  • 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: طباعة حقل name الخاص بـ keeper (التنسيق: "Name: <name>")

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

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

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

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

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