Mixins بسيطة
جزء من قسم Object Oriented Programming في رحلة Lua على Coddy — الدرس 51 من 70.
يسمح لك التركيب (Composition) بوضع كائنات داخل كائنات أخرى. ولكن ماذا لو كنت تريد مشاركة السلوك عبر أصناف (classes) غير مرتبطة؟
قد يحتاج كل من Player و MenuItem إلى طريقة :describe()، لكنهما لا يشتركان في فئة أب (parent class). هنا يأتي دور الـ mixins.
الـ mixin هو ببساطة جدول يحتوي على دوال. تقوم بـ "خلط" هذه الدوال داخل فئة (class) عن طريق نسخها إليها:
local Printable = {}
function Printable:describe()
print("Object: " .. tostring(self))
end
function Printable:log(message)
print("[LOG] " .. message)
endلتطبيق هذا الـ mixin على class، ستقوم بنسخ كل function من جدول الـ mixin إلى جدول الـ class:
local function applyMixin(class, mixin)
for key, value in pairs(mixin) do
class[key] = value
end
end
local Player = {}
Player.__index = Player
applyMixin(Player, Printable) -- الآن يمتلك Player كلاً من :describe() و :log()بعد تطبيق الـ mixin، يمكن لأي مثيل لـ Player استدعاء تلك الدوال كما لو كانت مُعرفة مباشرةً في الفئة:
local p = setmetatable({}, Player)
p:log("Player created") -- [LOG] تم إنشاء اللاعبعلى عكس الوراثة، لا تنشئ الـ mixins علاقة أب-ابن. أنت ببساطة تقوم بنسخ الدوال من جدول إلى آخر.
هذا يجعل الـ mixins مثالية لمشاركة سلوكيات الأدوات المساعدة—مثل طرق التسجيل (logging)، أو التسلسل (serialization)، أو الوصف (description)—عبر الفئات (classes) التي ليس بينها أي قاسم مشترك آخر.
التحدي
سهللنقم ببناء نظام وسوم (tagging system) باستخدام الـ mixins! ستقوم بإنشاء mixin باسم Taggable يمكن تطبيقه على أي فئة، مما يمنح الكائنات القدرة على إدارة مجموعة من الوسوم — وهو أمر مفيد لتصنيف العناصر، أو المنشورات، أو أي كيان يحتاج إلى تسميات.
ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:
Taggable.lua: أنشئ جدول mixin يحتوي على طريقتين (methods)::addTag(tag)— يضيف وسماً إلىself.tags(يفترض أن الجدول موجود):listTags()— يطبع جميع الوسوم بالتنسيقTags: tag1, tag2, tag3(أوTags: noneإذا كان فارغاً)
Article.lua: أنشئ فئة Article مع منشئ:new(title)يقوم بتخزين العنوان ويهيئ جدولtagsفارغاً. قم بتطبيق Taggable mixin لمنح مثيلات Article قدرات الوسم. قم بتضمين طريقة:getTitle()التي تعيد عنوان المقال.main.lua: اكتب الدالة المساعدةapplyMixinهنا (ستستخدمها في Article.lua عبر require). ثم اقرأ المدخلات لعنوان المقال ووسمين. أنشئ Article، وأضف كلا الوسمين إليه، واطبع العنوان، واعرض قائمة بجميع الوسوم.
ستتلقى ثلاثة مدخلات:
- عنوان المقال (سلسلة نصية)
- الوسم الأول المراد إضافته (سلسلة نصية)
- الوسم الثاني المراد إضافته (سلسلة نصية)
يجب أن يعرض مخرجك عنوان المقال متبوعاً بوسومه:
Title: {title}
Tags: {tag1}, {tag2}على سبيل المثال، إذا كانت المدخلات هي Lua Basics، و programming، و tutorial، فيجب أن يكون المخرج:
Title: Lua Basics
Tags: programming, tutorialتذكر: تعمل طرق الـ mixin لأن مثيلات Article تحتوي على جدول tags يمكن لطرق الـ mixin العمل عليه. الـ mixin لا ينشئ هذا الجدول — بل يقوم منشئ Article الخاص بك بذلك. الـ mixin يوفر فقط السلوك المشترك!
ورقة مرجعية
الـ mixin هو جدول يحتوي على دوال يمكن مشاركتها عبر فئات (classes) غير مرتبطة دون إنشاء علاقة أب-ابن (parent-child).
لإنشاء mixin، قم بتعريف جدول يحتوي على دوال (methods):
local Printable = {}
function Printable:describe()
print("Object: " .. tostring(self))
end
function Printable:log(message)
print("[LOG] " .. message)
endلتطبيق mixin على فئة (class)، قم بنسخ دواله إلى جدول الفئة:
local function applyMixin(class, mixin)
for key, value in pairs(mixin) do
class[key] = value
end
end
local Player = {}
Player.__index = Player
applyMixin(Player, Printable)بعد تطبيق الـ mixin، يمكن للكائنات (instances) استدعاء دوال الـ mixin:
local p = setmetatable({}, Player)
p:log("Player created") -- [LOG] Player createdتعد الـ Mixins مثالية لمشاركة سلوكيات الأدوات المساعدة مثل تسجيل الأحداث (logging)، أو التسلسل (serialization)، أو الوسم (tagging) عبر الفئات التي لا تشترك في الوراثة.
جرّب بنفسك
-- الملف الرئيسي لنظام الوسم
local Taggable = require('Taggable')
local Article = require('Article')
-- TODO: تنفيذ دالة applyMixin
-- يجب أن تقوم هذه الدالة بنسخ جميع الأساليب من جدول mixin إلى جدول الهدف
function applyMixin(target, mixin)
-- TODO: التكرار عبر mixin ونسخ كل أسلوب إلى الهدف
end
-- قراءة المدخلات
local title = io.read()
local tag1 = io.read()
local tag2 = io.read()
-- TODO: إنشاء مقال (Article) بالعنوان المعطى
-- TODO: إضافة كلا الوسمين إلى المقال
-- TODO: طباعة العنوان بالتنسيق: Title: {title}
-- TODO: سرد جميع الأوسمة باستخدام أسلوب listTags
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
4مشروع: بنك رقمي
إعداد المشروعطريقة الإيداع7تعدد الأشكال وإعادة التعريف
إعادة تعريف الدوالاستدعاء دوال الفئة الأبDuck Typingواجهة مشتركةالتحقق من النوعمراجعة - أدوار الموظفين2نمط النموذج الأولي للفئات
مفهوم النموذج الأوليالربط باستخدام __indexالمنشئ :new()تهيئة الخصائصنسخ مستقلةملخص - مصنع السيارات5تحميل المعاملات في OOP
جمع الكائناتطرح الكائناتدمج الكائناتمقارنة الكائنات (<, >)مراجعة - رياضيات المحفظة8التغليف
اصطلاحات التسميةاستخدام Closures للخصوصيةالوصول عبر Closuresجداول للقراءة فقطمنطق التحقق من الصحةمراجعة - الخزنة الآمنة11أنماط التصميم (مبسط)
دوال المصنعجدول Singletonنمط الـ Iteratorالمراقب (المستمع)مراجعة - مصنع الـ Logger3حالة الكائن وسلوكه
متغيرات النسخةدالات الجلب (Getter Methods)دالات التعيين (Setter Methods)الخصائص المحسوبةتنسيق النصوصالتحقق من المساواةمراجعة - درجة الطالب6أساسيات الوراثة
إعداد الوراثةوراثة الـ Methodsتوسيع الـ Constructorإضافة Methods للابنالمشترك مقابل الفريدملخص - التسلسل الهرمي للأشكال9التركيب و Mixins
علاقة "يحتوي على" (Has-A)التفويض (Delegation)Mixins بسيطةتطبيق Mixins متعددةMixins مقابل الوراثةملخص - تجميع الروبوت