0Pricing
Lua Academy · درس

أنماط Mixin

ركّب السلوك بنسخ الأساليب من جداول مصدر متعددة

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

ما هي Mixin؟

‏Mixin هي جدول من الدوال يمكن نسخه إلى عدة أصناف. وعلى خلاف الوراثة، تضيف Mixin سلوكًا من دون إنشاء علاقة بين صنف أب وصنف ابن. ويمكن للصنف دمج السلوكيات القادمة من عدة Mixin.

local Flyable={}
function Flyable:fly()
  print(self.name.." is flying!")
end
function Flyable:land()
  print(self.name.." landed.")
end

local Bird={} Bird.__index=Bird
-- Mix in Flyable
for k,v in pairs(Flyable) do Bird[k]=v end

function Bird.new(name)
  return setmetatable({name=name},Bird)
end
Bird.new("Eagle"):fly()

عدة Mixins

طبّق عدة Mixins على صنف واحد لتكوين سلوك معقد من مكونات بسيطة. يُسمّى هذا التركيب الأفقي، إذ تكون كل Mixin مستقلة.

local Serializable={}
function Serializable:toCSV(fields)
  local row={}
  for _,f in ipairs(fields) do row[#row+1]=tostring(self[f]) end
  return table.concat(row,",")
end

local Loggable={}
function Loggable:log(msg)
  print("["..os.date("%H:%M").."]["..self.name.."] "..msg)
end

local Entity={} Entity.__index=Entity
for k,v in pairs(Serializable) do Entity[k]=v end
for k,v in pairs(Loggable) do Entity[k]=v end

function Entity.new(n,v) return setmetatable({name=n,value=v},Entity) end
local e=Entity.new("item",42)
e:log("created"); print(e:toCSV({"name","value"}))

تعارضات Mixin

عندما تعرّف Mixinان اسم الدالة نفسه، تسود Mixin المطبقة أخيرًا. عالج التعارضات صراحةً باختيار إصدار إحدى Mixin، أو غلّفهما لاستدعائهما معًا.

local A={speak=function(self) print(self.name.." (A)") end}
local B={speak=function(self) print(self.name.." (B)") end}

local C={} C.__index=C
for k,v in pairs(A) do C[k]=v end
for k,v in pairs(B) do C[k]=v end  -- B.speak overwrites A.speak

-- Explicit resolution:
local origA, origB = A.speak, B.speak
function C:speak() origA(self); origB(self) end  -- call both

local c=setmetatable({name="X"},C)
c:speak()

المساعد include()

تجعل الدالة المساعدة تطبيق Mixins أكثر سهولة: include(Class, Mixin) تنسخ دوال Mixin إلى الصنف، وتتجاوز الحقول الموجودة مسبقًا، ولذلك لا تستبدل الدوال الحالية.

local function include(cls, mixin)
  for k, v in pairs(mixin) do
    if cls[k] == nil then
      cls[k] = v
    end
  end
  return cls
end

local Comparable={
  equals=function(a,b) return a:compareTo(b)==0 end,
  lt=function(a,b) return a:compareTo(b)<0 end,
}

local Temp={} Temp.__index=Temp
include(Temp, Comparable)
function Temp.new(c) return setmetatable({c=c},Temp) end
function Temp:compareTo(other) return self.c-other.c end

local a,b=Temp.new(100),Temp.new(50)
print(b.lt and b:lt(a))  -- true (b < a)

Mixin مع حالة

تُهيّئ Mixins التي تحتاج إلى حالة خاصة بكل مثيل هذه الحالة في دالة init الخاصة بها. ويستدعي مُنشئ الصنف دوال init الخاصة بـ Mixins لإعداد حالة كل Mixin في المثيل.

local Observable={}
function Observable._init(self)
  self._listeners={}
end
function Observable:on(event,fn)
  self._listeners[event]=self._listeners[event] or {}
  self._listeners[event][#self._listeners[event]+1]=fn
end
function Observable:emit(event,...)
  for _,fn in ipairs(self._listeners[event] or {}) do fn(...) end
end

local Button={} Button.__index=Button
for k,v in pairs(Observable) do Button[k]=v end

function Button.new(label)
  local self=setmetatable({label=label},Button)
  Observable._init(self)
  return self
end

local btn=Button.new("OK")
btn:on("click",function() print("Clicked!") end)
btn:emit("click")

وحدات Mixin

ضع سلوك Mixin في ملفات وحدات منفصلة. استوردها في وحدة الصنف وطبّقها. يحافظ ذلك على وجود كل جانب من جوانب السلوك في ملفه الخاص، وينظّم قاعدة الشيفرة.

-- In a real project:
-- local Loggable = require("mixins.loggable")
-- local Cacheable = require("mixins.cacheable")
-- local MyClass = {}
-- for k,v in pairs(Loggable) do MyClass[k]=v end
-- for k,v in pairs(Cacheable) do MyClass[k]=v end

local TimestampMixin={}
function TimestampMixin:setCreated() self.createdAt=os.time() end
function TimestampMixin:age() return os.time()-self.createdAt end

local Record={} Record.__index=Record
for k,v in pairs(TimestampMixin) do Record[k]=v end
function Record.new(data)
  local self=setmetatable(data or {},Record)
  self:setCreated()
  return self
end
print(Record.new({}).createdAt>0)

Mixins المستندة إلى الأدوار

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

local Roles={}
Roles.admin={deleteUser=function(self) print(self.name.." deletes user") end}
Roles.editor={editPost=function(self) print(self.name.." edits post") end}
Roles.viewer={viewPost=function(self) print(self.name.." views post") end}

local User={} User.__index=User
function User.new(name,...)
  local self=setmetatable({name=name},User)
  for _,role in ipairs({...}) do
    for k,v in pairs(Roles[role] or {}) do self[k]=v end
  end
  return self
end

local alice=User.new("Alice","admin","editor")
alice:deleteUser(); alice:editPost()

التحقق من تطبيق Mixin

تتبّع Mixins المطبقة على صنف باستخدام مجموعة. يمنع ذلك التطبيق المزدوج، ويتيح فحص إمكانات الصنف.

local function applyMixin(cls, mixin, name)
  cls._mixins = cls._mixins or {}
  if cls._mixins[name] then return end  -- already applied
  for k,v in pairs(mixin) do
    if k~="_init" then cls[k]=cls[k] or v end
  end
  cls._mixins[name] = true
end

local Fly={fly=function(self) print(self.name.." flies") end}
local Bird={} Bird.__index=Bird
applyMixin(Bird,Fly,"Fly")
applyMixin(Bird,Fly,"Fly")  -- no-op, already applied
print(Bird._mixins.Fly)  -- true

Mixins مقابل الوراثة مقابل التركيب

هناك ثلاثة أساليب للتركيب: الوراثة، أي «هوَ» مع تسلسل هرمي صارم؛ وMixins، أي نسخ الدوال ضمن بنية مسطحة؛ والتركيب، أي «يمتلك» مع التفويض. اختر الأسلوب بناءً على العلاقة: الحيوان هوَ مخلوقًا (وراثة)، والمخلوق يستطيع الطيران (Mixin)، والسيارة تمتلك محركًا (تركيب).

-- Inheritance: "is-a"
-- local Dog = extends(Animal)

-- Mixin: "can-do"
-- include(Dog, Swimmable)

-- Composition: "has-a"
-- local Dog = {}
-- function Dog.new()
--   return {collar=Collar.new(),chip=Chip.new()}
-- end

print("Pick the right tool for the relationship")

قالب Mixin

قالب Mixin قياسي: يحتوي جدول Mixin على الدوال، و_init اختيارية للحالة الخاصة بكل مثيل، وحقل name لاكتشاف التعارضات. يؤدي اتباع نمط متسق إلى جعل Mixins قابلة للتركيب عبر المشاريع.

-- Standard mixin template:
local EventMixin = {}
EventMixin._name = "EventMixin"

function EventMixin._init(self)
  self._events = {}
end

function EventMixin:on(e, fn)
  self._events[e] = self._events[e] or {}
  self._events[e][#self._events[e]+1] = fn
end

function EventMixin:trigger(e, ...)
  for _, fn in ipairs(self._events[e] or {}) do fn(...) end
end

-- return EventMixin
print("EventMixin ready for use")

سؤال حول Mixin

كيف تطبّق Mixin على صنف؟

مراجعة: أنماط Mixin

تنسخ Mixins الدوال من جدول مصدر إلى الصنف الهدف، فتُركّب السلوك من دون وراثة. استخدمها للجوانب المشتركة بين مكونات متعددة، مثل التسلسل والتسجيل ومعالجة الأحداث.

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

هل درس «أنماط Mixin» مجاني؟

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

ماذا ستتعلم في «أنماط Mixin»؟

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

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

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

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

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

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

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

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

  1. الوراثة الأحادية مع تسلسل __index
  2. استدعاء أساليب الفئة الأصلية باستخدام super
  3. أنماط Mixin
  4. إعادة التعريف وتعدد الأشكال
← العودة إلى Lua Academy