أنماط 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) -- trueMixins مقابل الوراثة مقابل التركيب
هناك ثلاثة أساليب للتركيب: الوراثة، أي «هوَ» مع تسلسل هرمي صارم؛ و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 يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- الوراثة الأحادية مع تسلسل __index
- استدعاء أساليب الفئة الأصلية باستخدام super
- أنماط Mixin
- إعادة التعريف وتعدد الأشكال