0Pricing
Lua Academy · درس

كتابة ملف وحدة

أنشئ وحدة بإرجاع جدول من الدوال من ملف ‎.lua

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

النمط الأساسي للوحدة

النمط القياسي للوحدات في Lua هو إنشاء جدول محلي M، ثم إضافة الدوال والقيم إليه، وإعادته في النهاية. يمثّل الجدول واجهة الوحدة العامة. أما جميع المتغيرات المحلية الأخرى في الملف فهي خاصة.

-- stringutils.lua
local M = {}

function M.trim(s)
  return s:match("^%s*(.-)%s*$")
end

function M.split(s, sep)
  local t = {}
  for p in s:gmatch("[^"..sep.."]+") do t[#t+1]=p end
  return t
end

return M

-- Usage:
-- local su = require("stringutils")
-- print(su.trim("  hello  "))

الحالة الخاصة

المتغيرات المعلنة باستخدام local في ملف الوحدة تكون خاصة، ولا يمكن للمستدعين الوصول إليها. ويمكن للدوال الموجودة في الوحدة الوصول إلى الحالة الخاصة باعتبارها upvalues. هذه هي آلية التغليف في Lua.

-- counter.lua
local M = {}
local count = 0   -- private state

function M.increment(n)
  count = count + (n or 1)
end

function M.reset()
  count = 0
end

function M.get()
  return count
end

return M

وحدة مع التهيئة

تحتاج بعض الوحدات إلى التهيئة، مثل الإعدادات والاتصالات. ضعوا رمز التهيئة على مستوى الوحدة (في بداية الملف) أو داخل دالة صريحة هي M.init(). يُنفَّذ الأسلوب الأول مرة واحدة عند طلب الوحدة أول مرة، بينما يتطلب init() استدعاءً صريحًا.

-- cache.lua
local M = {}
local store = {}   -- initialized at load time
local hits = 0
local misses = 0

function M.get(key)
  if store[key] ~= nil then
    hits = hits + 1
    return store[key]
  end
  misses = misses + 1
  return nil
end

function M.set(key, val) store[key] = val end
function M.stats() return {hits=hits, misses=misses} end

return M

وحدة مع صنف

يمكن للوحدة تصدير صنف: أي جدول يحتوي على دالة إنشاء. يحتوي جدول الوحدة المُعاد على الدالة new()، وقد يعمل أيضًا باعتباره metatable للصنف.

-- point.lua
local Point = {}
Point.__index = Point

function Point.new(x, y)
  return setmetatable({x=x, y=y}, Point)
end

function Point:distance(other)
  local dx, dy = self.x-other.x, self.y-other.y
  return math.sqrt(dx*dx + dy*dy)
end

function Point:__tostring()
  return string.format("(%g,%g)", self.x, self.y)
end

return Point

-- Usage:
-- local Point = require("point")
-- local p = Point.new(3, 4)

ثوابت الوحدة

صدّروا الثوابت بإضافتها إلى جدول الوحدة. جرت العادة على كتابة الثوابت بأحرف كبيرة. وبما أن Lua لا تحتوي على const، يمكن للمستخدمين تعديلها تقنيًا، لكن تسمية الأحرف الكبيرة تشير إلى «عدم تغييرها».

-- colors.lua
local M = {}

M.RED   = {r=255, g=0,   b=0}
M.GREEN = {r=0,   g=255, b=0}
M.BLUE  = {r=0,   g=0,   b=255}
M.WHITE = {r=255, g=255, b=255}
M.BLACK = {r=0,   g=0,   b=0}

function M.toHex(c)
  return string.format("#%02X%02X%02X", c.r, c.g, c.b)
end

return M

إصدارات الوحدة

أدرجوا حقل إصدار في الوحدة. يمكن للمستدعين التحقق من الإصدار لضمان التوافق. استخدموا الإصدار الدلالي (major.minor.patch).

-- mylib.lua
local M = {}
M._VERSION = "1.2.3"
M._NAME = "mylib"
M._DESCRIPTION = "My Lua library"

function M.hello(name)
  return "Hello, " .. (name or "World") .. "!"
end

return M

-- Usage:
local mylib = require("mylib")
print(mylib._VERSION)   -- 1.2.3
print(mylib.hello("Lua"))

الوحدات الفرعية

تُقسَّم المكتبات الكبيرة إلى وحدات فرعية. قد تتطلب الوحدة الرئيسية الوحدات الفرعية وتعيد تصديرها، أو قد تُستخدم كل وحدة فرعية بشكل مستقل. نظّموا الملفات في أدلة تطابق مسار الوحدة.

-- mylib/init.lua  (loaded by require("mylib"))
local M = {}

M.strings = require("mylib.strings")
M.tables  = require("mylib.tables")
M.math    = require("mylib.math")

M._VERSION = "2.0.0"

return M

-- Users can require the whole library:
-- local mylib = require("mylib")
-- mylib.strings.trim(...)

-- Or individual sub-modules:
-- local strs = require("mylib.strings")

وحدة مع metatable

اجعلوا الوحدة قابلة للاستدعاء بمنحها metatable يحتوي على __call. يفيد ذلك الوحدات التي تكون في الأساس دوال، لكنها تحتوي أيضًا على أدوات مساعدة؛ إذ تكون العملية «الرئيسية» هي الاستدعاء، بينما تكون الأدوات حقولًا.

-- format.lua
local M = {}
setmetatable(M, {__call = function(_, fmt, ...)
  return string.format(fmt, ...)
end})

function M.pad(s, width, char)
  char = char or " "
  return string.rep(char, math.max(0, width - #s)) .. s
end

return M

-- Usage:
-- local fmt = require("format")
-- print(fmt("%.2f", 3.14))   -- 3.14
-- print(fmt.pad("42", 5))    --    42

التحميل الكسول للوحدات الفرعية

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

-- biglib.lua
local M = {}
local submodules = {"strings", "tables", "math", "io"}

setmetatable(M, {
  __index = function(t, k)
    for _, name in ipairs(submodules) do
      if name == k then
        local mod = require("biglib." .. k)
        rawset(t, k, mod)
        return mod
      end
    end
    return nil
  end
})

return M

-- Loads biglib.strings only when accessed:
-- local lib = require("biglib")
-- lib.strings.trim(...)

نمط اختبار الوحدة

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

-- utils.lua
local M = {}

function M.clamp(v, lo, hi)
  return math.max(lo, math.min(hi, v))
end

-- Self-test: only runs when executed directly
if debug.getinfo(2, "S") == nil then
  -- Running as main script, not required
  print("Testing clamp...")
  assert(M.clamp(5, 0, 10) == 5)
  assert(M.clamp(-1, 0, 10) == 0)
  assert(M.clamp(15, 0, 10) == 10)
  print("All tests passed!")
end

return M

تحقق سريع

ما الطريقة القياسية لتعريف الحالة الخاصة في وحدة Lua؟

مراجعة: كتابة الوحدات

الملخص:

  • النمط: local M = {} ... return M
  • المتغيرات المحلية في الملف = خاصة؛ وحقول M = واجهة عامة
  • يُنفَّذ رمز مستوى الوحدة مرة واحدة عند أول require
  • صدّروا الأصناف من خلال دالة إنشاء في جدول الوحدة
  • التحميل الكسول للوحدات الفرعية باستخدام __index
  • أدرجوا _VERSION للتحقق من التوافق

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

هل درس «كتابة ملف وحدة» مجاني؟

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

ماذا ستتعلم في «كتابة ملف وحدة»؟

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

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

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

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

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

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

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

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

  1. الدالة require
  2. كتابة ملف وحدة
  3. ‏package.path وpackage.cpath
  4. أنماط الوحدات وأفضل الممارسات
← العودة إلى Lua Academy