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