استخدام 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 وتعديلها، مما يمنحك وصولًا مضبوطًا مع الحفاظ على التغليف الحقيقي.
التحدي
سهللنبنِ فئة SecretKeeper توضّح الخصوصية الحقيقية للبيانات باستخدام الإغلاقات! بخلاف اصطلاح الشرطة السفلية من الدرس السابق، يجعل هذا النهج البيانات غير قابلة للوصول إليها فعليًا من خارج الكائن.
ستنظّم الشيفرة البرمجية في ملفين:
SecretKeeper.lua: أنشئ فئة تُخزَّن فيها القيمة السرية في متغير محلي داخل المُنشئ، وليس علىself. يجب أن يخزّن المُنشئ:new(secretValue)السر في متغير محلي لا يوجد إلا داخل الإغلاق. في الوقت الحالي، لن تكون لدى الكائن أي وسيلة للوصول إلى هذا السر (سيأتي ذلك في الدرس التالي!)، ولكن يجب عليك أيضًا تخزين حقل عامnameمضبوطًا على"Keeper"في الكائن حتى نتمكن من التحقق من أن الكائن يعمل.main.lua: اطلب وحدة SecretKeeper واقرأ قيمة سرية من الإدخال. أنشئ كائنًا من SecretKeeper باستخدام ذلك السر. ثم وضّح أن السر خاص فعلًا من خلال طباعة ثلاثة أشياء:- حقل
nameالخاص بـ keeper (يجب أن يطبعKeeper) - ما يحدث عند محاولة الوصول إلى
keeper.secret(يجب أن تكون النتيجةnil) - ما يحدث عند محاولة الوصول إلى
keeper._secret(يجب أن تكون النتيجة أيضًاnil)
- حقل
ستتلقى إدخالًا واحدًا:
- القيمة السرية المراد تخزينها (مثلًا،
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>")
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
4مشروع: بنك رقمي
إعداد المشروعطريقة الإيداع7تعدد الأشكال وإعادة التعريف
إعادة تعريف الدوالاستدعاء دوال الفئة الأبDuck Typingواجهة مشتركةالتحقق من النوعمراجعة - أدوار الموظفين2نمط النموذج الأولي للفئات
مفهوم النموذج الأوليالربط باستخدام __indexالمنشئ :new()تهيئة الخصائصنسخ مستقلةملخص - مصنع السيارات5تحميل المعاملات في OOP
جمع الكائناتطرح الكائناتدمج الكائناتمقارنة الكائنات (<, >)مراجعة - رياضيات المحفظة8التغليف
اصطلاحات التسميةاستخدام Closures للخصوصيةالوصول عبر Closuresجداول للقراءة فقطمنطق التحقق من الصحةمراجعة - الخزنة الآمنة11أنماط التصميم (مبسط)
دوال المصنعجدول Singletonنمط الـ Iteratorالمراقب (المستمع)مراجعة - مصنع الـ Logger3حالة الكائن وسلوكه
متغيرات النسخةدالات الجلب (Getter Methods)دالات التعيين (Setter Methods)الخصائص المحسوبةتنسيق النصوصالتحقق من المساواةمراجعة - درجة الطالب6أساسيات الوراثة
إعداد الوراثةوراثة الـ Methodsتوسيع الـ Constructorإضافة Methods للابنالمشترك مقابل الفريدملخص - التسلسل الهرمي للأشكالتدرّب بنفسك: مترجم Lua عبر الإنترنت