0Pricing
Lua Academy · درس

أنماط الوحدات وأفضل الممارسات

استخدم النمط local M = {} واعرض واجهة API عامة بطريقة واضحة

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

نمط M = {}

النمط العام لوحدات Lua هو تعريف جدول محلي، وملؤه، ثم إعادته. توضع جميع الرموز العامة في M، بينما تكون جميع الدوال المساعدة الخاصة متغيرات محلية عادية. هذا النمط واضح ومختصر ويعمل في كل مكان.

-- The canonical module pattern
local M = {}

-- Private helper (not exported)
local function validate(x)
  return type(x) == "number" and x >= 0
end

-- Public API
function M.sqrt(x)
  assert(validate(x), "expected non-negative number")
  return math.sqrt(x)
end

M.PI = math.pi

return M

وحدة تشير إلى نفسها

داخل الوحدة، يمكن للدوال استدعاء دوال أخرى فيها إما بالاسم (M.foo()) أو باعتبارها متغيرات محلية. استخدام المتغيرات المحلية أسرع قليلًا، بينما يتيح استخدام M.foo() للمستخدمين تجاوز M.foo بحيث يستخدم الاستدعاء الداخلي التجاوز (تعديل monkey patching).

local M = {}

-- Option A: use M.foo inside (allows override)
function M.double(n) return M.multiply(n, 2) end
function M.multiply(a, b) return a * b end

-- Option B: use local function (faster, no override)
local function mul(a, b) return a * b end
function M.triple(n) return mul(n, 3) end

return M

نمط Singleton

يمكن للوحدة أن تعمل باعتبارها Singleton، أي أن لها حالة داخلية قابلة للتغيير ومشتركة بين جميع المستدعين. وبما أن require يخزّن الوحدة مؤقتًا، تحصل جميع استدعاءات require("mod") على الكائن نفسه بالحالة نفسها.

-- config.lua (singleton)
local M = {}
local _config = {env="dev", logLevel="info"}

function M.set(key, val)
  _config[key] = val
end

function M.get(key)
  return _config[key]
end

function M.load(t)
  for k,v in pairs(t) do _config[k]=v end
end

return M

-- All callers share the same config:

الوحدة باعتبارها مساحة أسماء

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

-- constants.lua
local M = {
  HTTP = {
    OK=200, CREATED=201, NO_CONTENT=204,
    BAD_REQUEST=400, UNAUTHORIZED=401,
    NOT_FOUND=404, SERVER_ERROR=500,
  },
  COLORS = {RED="#FF0000", GREEN="#00FF00", BLUE="#0000FF"},
  MAX_RETRIES = 3,
  TIMEOUT_SEC = 30,
}
return M

-- local C = require("constants")
-- if status == C.HTTP.NOT_FOUND then ...

وحدة Factory

هي وحدة تصدّر دالة Factory بدلًا من جدول عادي. تنشئ Factory مثيلات جديدة وتعيدها، ولكل مثيل حالته الخاصة. هذا هو نمط الصنف على مستوى الوحدة.

-- logger.lua
local M = {}

function M.new(name, level)
  level = level or "info"
  local levels = {debug=1,info=2,warn=3,error=4}
  local self = {}
  
  function self.log(msgLevel, msg)
    if levels[msgLevel] >= levels[level] then
      print(string.format("[%s][%s] %s", name, msgLevel:upper(), msg))
    end
  end
  
  function self.info(msg)  self.log("info",  msg) end
  function self.warn(msg)  self.log("warn",  msg) end
  function self.error(msg) self.log("error", msg) end
  
  return self
end

return M

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

تحتاج بعض الوحدات إلى الإعداد قبل استخدامها. وفّروا دالة M.init(config) تخزّن الإعدادات في الحالة الخاصة بالوحدة. يتيح ذلك حقن التبعيات وقابلية الاختبار.

-- db.lua
local M = {}
local pool = nil

function M.init(config)
  pool = {
    host = config.host or "localhost",
    port = config.port or 5432,
    connections = {},
  }
  print("DB initialized:", pool.host, pool.port)
end

function M.query(sql)
  assert(pool, "call db.init() first")
  -- ... execute query
  return {}
end

return M

وحدة غير قابلة للتغيير

امنعوا المستخدمين من تعديل واجهة الوحدة عن طريق الخطأ باستخدام __newindex لحظر جميع عمليات الكتابة. يفيد ذلك خصوصًا في وحدات المكتبات، حيث قد يؤدي monkey-patching غير المقصود إلى تعطيل الوظائف.

local function freeze(t)
  return setmetatable({}, {
    __index = t,
    __newindex = function(_, k, _)
      error("module is read-only, cannot set: " .. tostring(k), 2)
    end
  })
end

local M = {}
function M.add(a, b) return a + b end
function M.sub(a, b) return a - b end

return freeze(M)

التوثيق باستخدام LDoc

من الاصطلاحات الشائعة لتوثيق وحدات Lua استخدام تعليقات بأسلوب LDoc مع البادئة ---. ورغم أن اللغة لا تفرض هذه التعليقات، فإنها تتيح لأدوات إنشاء التوثيق إنتاج مستندات API تلقائيًا.

--- A utility module for string operations.
-- @module stringutils
local M = {}

--- Trim leading and trailing whitespace.
-- @param s string The input string.
-- @return string The trimmed string.
function M.trim(s)
  return s:match("^%s*(.-)%s*$")
end

--- Count occurrences of a substring.
-- @param str string The string to search.
-- @param sub string The substring to count.
-- @return number Count of occurrences.
function M.count(str, sub)
  local _, n = str:gsub(sub, "")
  return n
end

return M

اختبار الوحدات

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

-- test/test_stringutils.lua
local su = require("stringutils")

local function test(name, fn)
  local ok, err = pcall(fn)
  if ok then print("[PASS] " .. name)
  else   print("[FAIL] " .. name .. ": " .. err)
  end
end

test("trim removes spaces", function()
  assert(su.trim("  hello  ") == "hello")
end)

test("trim empty string", function()
  assert(su.trim("") == "")
end)

test("count occurrences", function()
  assert(su.count("banana", "a") == 3)
end)

تركيب الوحدات

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

-- app.lua (main entry point)
local config = require("config")
local db     = require("db")
local server = require("server")

-- Configure from environment
config.load({
  dbHost = os.getenv("DB_HOST") or "localhost",
  port   = tonumber(os.getenv("PORT")) or 8080,
})

-- Wire modules together
db.init({host=config.get("dbHost"), port=5432})
server.init({port=config.get("port"), db=db})
server.start()

تحقق سريع

ما الغرض الرئيسي من نمط الوحدة local M = {} ... return M؟

مراجعة: أفضل ممارسات الوحدات

الملخص:

  • استخدموا دائمًا local M = {} ... return M
  • الخاص = المتغيرات المحلية على مستوى الملف؛ والعام = حقول M
  • يخزّن require مثيل الوحدة في نمط Singleton
  • استخدموا دوال Factory للحالة الخاصة بكل مثيل
  • جمّدوا الوحدات باستخدام __newindex لمنع التعديل
  • اختبروا في ملفات منفصلة، ووثّقوا باستخدام تعليقات ---

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

هل درس «أنماط الوحدات وأفضل الممارسات» مجاني؟

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

ماذا ستتعلم في «أنماط الوحدات وأفضل الممارسات»؟

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

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

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

كم من الوقت يستغرق درس «أنماط الوحدات وأفضل الممارسات»؟

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

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

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

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

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