0Pricing
Lua Academy · درس

التغليف باستخدام الإغلاقات

أخفِ الحالة الخاصة باستخدام أنماط الكائنات المعتمدة على الإغلاقات

التغليف باستخدام الإغلاقات درس مجاني في Lua Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Lua Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Lua Academy 4 دروس في المجموع.

كائنات قائمة على الإغلاقات

بديل للبرمجة كائنية التوجه باستخدام جداول البيانات الوصفية: استخدم إغلاقًا لإخفاء الحالة. يكون الكائن جدولًا من الدوال؛ وتلتقط جميع الدوال المتغيرات الخاصة المحيطة بها. ولا حاجة إلى جدول بيانات وصفية. حالة خاصة فعلًا لا يمكن الوصول إليها من الخارج.

local function makeCounter(start)
  local count = start or 0   -- truly private
  return {
    increment = function() count=count+1 end,
    decrement = function() count=count-1 end,
    get       = function() return count end,
    reset     = function() count=0 end,
  }
end

local c = makeCounter(10)
c.increment(); c.increment()
print(c.get())   -- 12
print(c.count)   -- nil (truly private!)

الخاص مقابل العام

مع الإغلاقات، يكون الفرق بين الخاص والعام مطلقًا: تشترك الإغلاقات في القيمة المحيطة (الخاصة)، ولا ترى الشفرة الخارجية إلا الجدول المُعاد (العام). ولا توجد طريقة للوصول إلى المتغيرات الخاصة دون المرور بواجهة البرمجة العامة.

local function makeAccount(initial)
  local balance = initial
  local transactions = {}
  
  local function record(type, amount)
    transactions[#transactions+1] = {type=type,amount=amount}
  end
  
  return {
    deposit  = function(n) record("D",n); balance=balance+n end,
    withdraw = function(n)
      if n>balance then error("insufficient funds") end
      record("W",n); balance=balance-n
    end,
    balance  = function() return balance end,
    history  = function() return transactions end,
  }
end

الإغلاقات المشتركة

تشترك الدوال المتعددة في الجدول المُعاد نفسه في المتغيرات المحيطة نفسها. وهذه هي طريقة مشاركة الحالة بين الأساليب دون كشفها للعامة. ترى جميع الأساليب المتغيرات الخاصة نفسها.

local function makeQueue()
  local items = {}  -- shared by all methods
  local size = 0
  return {
    enqueue = function(v) size=size+1; items[size]=v end,
    dequeue = function()
      if size==0 then return nil end
      local head=1
      while items[head]==nil do head=head+1 end
      local v=items[head]; items[head]=nil; return v
    end,
    isEmpty = function() return size==0 end,
    count   = function() return size end,
  }
end

الإغلاقات مقابل جداول البيانات الوصفية

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

-- Closure: per-instance functions (more memory)
local function makePoint(x,y)
  return {
    x = function() return x end,
    y = function() return y end,
    distance = function()
      return math.sqrt(x*x+y*y)
    end
  }
end

-- Metatable: shared methods (less memory)
-- local Point = {}; Point.__index = Point
-- function Point.new(x,y) return setmetatable({x=x,y=y},Point) end

تعديل الإغلاقات

تتيح الإغلاقات التي تحيط بمتغيرات قابلة للتعديل إنشاء كائنات ذات حالة دون استخدام جداول على الإطلاق. ويمكن لإغلاق واحد الاحتفاظ بالحالة وحساب النتائج.

local function makeAccumulator()
  local sum, count = 0, 0
  return {
    add = function(n) sum=sum+n; count=count+1 end,
    mean = function()
      return count>0 and sum/count or 0
    end,
    total = function() return sum end,
    n     = function() return count end,
  }
end

local acc = makeAccumulator()
for _, v in ipairs({10,20,30,40,50}) do
  acc.add(v)
end
print(acc.total(),acc.mean(),acc.n())  -- 150  30.0  5

المُنشئ مع التحقق

تتحقق المُنشئات القائمة على الإغلاقات من المدخلات في دالة المصنع، لا في أسلوب. وتؤدي المدخلات غير الصالحة إلى إعادة المصنع للقيمة nil أو لخطأ قبل إنشاء أي كائن.

local function makeRange(lo, hi)
  assert(lo<=hi,"lo must be <= hi")
  return {
    contains = function(v) return v>=lo and v<=hi end,
    clamp    = function(v) return math.max(lo,math.min(hi,v)) end,
    lo       = function() return lo end,
    hi       = function() return hi end,
    span     = function() return hi-lo end,
  }
end

local r = makeRange(0,100)
print(r.contains(50))   -- true
print(r.clamp(150))     -- 100

الأحداث مع الإغلاقات

باثّ أحداث منفذ باستخدام الإغلاقات: تكون قائمة المعالجات خاصة؛ أما add/remove/emit فهي واجهة البرمجة العامة.

local function makeEmitter()
  local handlers = {}
  return {
    on = function(event, fn)
      handlers[event] = handlers[event] or {}
      handlers[event][#handlers[event]+1] = fn
    end,
    emit = function(event, ...)
      for _, fn in ipairs(handlers[event] or {}) do
        fn(...)
      end
    end,
    off = function(event) handlers[event] = {} end,
  }
end

local emitter = makeEmitter()
emitter.on("data", function(v) print("Got:", v) end)
emitter.emit("data", 42)   -- Got: 42

الحفظ المؤقت باستخدام الإغلاق

غلاف memoize باستخدام الإغلاقات: تكون ذاكرة التخزين المؤقت خاصة، والدالة المغلّفة خاصة، ولا تُعاد إلا النسخة التي تستخدم الحفظ المؤقت.

local function memoize(fn)
  local cache = {}
  return function(...)
    local key = table.concat({...}, ",")
    if cache[key] == nil then
      cache[key] = fn(...)
    end
    return cache[key]
  end
end

local expensiveFib
expensiveFib = memoize(function(n)
  if n<=1 then return n end
  return expensiveFib(n-1)+expensiveFib(n-2)
end)

for i=0,10 do io.write(expensiveFib(i).." ") end
print()

توسيع كائنات الإغلاقات

يمكنك توسيع كائن قائم على إغلاق بإنشاء كائن جديد يستدعي أساليب الكائن الأصلي. وهذا يشبه التغليف، لا التوارث — إذ يُفوَّض إلى الكائن الأصلي.

local function makeTimedCounter(start)
  local base = makeCounter and makeCounter(start) or (function()
    local c=start or 0
    return {inc=function()c=c+1 end,get=function()return c end}
  end)()
  local callCount = 0
  return {
    increment = function()
      callCount=callCount+1
      base.inc()
    end,
    get = base.get,
    calls = function() return callCount end,
  }
end

متى تستخدم الإغلاقات

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

-- Guideline summary:
-- Closures: true privacy, no metatable, self-contained
-- Metatables: shared methods, inheritance, instanceof checks

-- Rule of thumb:
-- < 100 instances with private state -> closures
-- > 100 instances OR need inheritance -> metatables

print("Both patterns are valid Lua OOP.")

تحقق سريع

ما الميزة الرئيسية للكائنات القائمة على الإغلاقات مقارنةً بالكائنات القائمة على جداول البيانات الوصفية؟

مراجعة: الإغلاقات للتغليف

الملخص:

  • نمط الإغلاق: تعيد دالة المصنع جدولًا من الأساليب؛ وتكون الحالة متغيرًا محيطًا
  • خصوصية حقيقية: لا توجد طريقة للوصول إلى المتغيرات المحيطة خارجيًا
  • تشترك جميع الأساليب المُعادة في المتغيرات المحيطة الخاصة نفسها
  • المقايضة: ذاكرة أكبر لكل نسخة مقابل خصوصية أفضل
  • استخدمه في الشفرة الحساسة أمنيًا أو التصاميم ذات الأسلوب الوظيفي

الأسئلة الشائعة

هل درس «التغليف باستخدام الإغلاقات» مجاني؟

نعم — نص درس «التغليف باستخدام الإغلاقات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Lua Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Lua Academy 4 دروس في المجموع.

ماذا ستتعلم في «التغليف باستخدام الإغلاقات»؟

أخفِ الحالة الخاصة باستخدام أنماط الكائنات المعتمدة على الإغلاقات تتمرن على Lua Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Lua Academy؟

لا تُشترط خبرة سابقة. Lua Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «التغليف باستخدام الإغلاقات»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Lua Academy هذا؟

نعم. كل درس في Lua Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. الفئات عبر جداول البيانات الوصفية
  2. المنشئات وnew()
  3. أساليب النسخ وself
  4. التغليف باستخدام الإغلاقات
← العودة إلى Lua Academy