Mixins مقابل الوراثة
جزء من قسم البرمجة كائنية التوجه في رحلة Lua على Coddy. الدرس 53 من 70.
لقد تعلّمت طريقتين لمشاركة السلوك: تنشئ الوراثة علاقة «is-a» من خلال سلسلة النماذج الأولية، بينما تنسخ المزيجات الدوال مباشرةً إلى فئة. لكن متى ينبغي أن تستخدم كل نهج؟
استخدم الوراثة عندما تشترك الكائنات في هوية أساسية. إن Bird هو Animal. من المنطقي أن يرث Bird سلوكيات الحيوانات الأساسية مثل :eat() أو :sleep(). العلاقة دائمة وتحدد ماهية الكائن بشكل أساسي.
استخدم mixins عندما تريد إضافة قدرة لا تحدد الهوية الأساسية للكائن.
ليست كل birds قادرة على fly: طيور البطريق والنعام هي birds، لكنها لا تقوم بـ fly. إن الطيران هو capability، وليس سمةً تحدد كونها bird. يتيح لك نمط Flyable mixin إضافة الطيران إلى birds التي تحتاج إليه، من دون فرضه على all منها.
يعمل النهجان معًا بشكل رائع:
-- Bird يرث من Animal (علاقة is-a)
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- Flyable هي قدرة (mixin)
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
-- طبّق الـ mixin فقط على الطيور التي تستطيع الطيران
applyMixin(Bird, Flyable)يمنحك هذا النمط أفضل ما في العالمين: تسلسلاً هرميًا واضحًا للفئات من أجل الهوية المشتركة، بالإضافة إلى إمكانات مرنة يمكن مزجها عند الحاجة.
التحدي
سهللنبنِ نظامًا بيئيًا صغيرًا من الكائنات يوضّح متى نستخدم الوراثة ومتى نستخدم الخلطات البرمجية! ستنشئ فئة أساسية Animal، وBird ترث منها، وخلطة Swimmable، لأن جميع الطيور حيوانات، لكن ليست جميع الطيور قادرة على السباحة.
ستنظّم شيفرتك عبر أربعة ملفات:
Animal.lua: أنشئ فئةAnimalالأساسية مع مُنشئ:new(name)يخزّن الاسم. أضف طريقة:eat()تطبع{name} is eating. يمثّل هذا الهوية الأساسية المشتركة بين جميع الحيوانات.Swimmable.lua: أنشئ خلطة تحتوي على طريقة:swim()تطبع{self.name} is swimming. هذه قدرة، وليست جميع الكائنات قادرة على السباحة، لذلك لا ينبغي أن تكون جزءًا من سلسلة الوراثة.Bird.lua: أنشئ فئةBirdترث منAnimal. يجب أن يستدعي المُنشئ:new(name, canSwim)مُنشئ الفئة الأب ويخزّن ما إذا كان هذا الطائر قادرًا على السباحة. طبّق خلطةSwimmableعلى فئةBird. أضف طريقة:chirp()تطبع{name} chirps!. هذا سلوك خاص بالطيور.main.lua: اكتب هنا الدالة المساعدةapplyMixin. اقرأ مُدخلين لاسم الطائر وما إذا كان قادرًا على السباحة (yesأوno). أنشئ نسخة منBird، ثم اعرض قدراتها: استدعِ:eat()(الموروثة منAnimal)، واستدعِ:chirp()(طريقتها الخاصة فيBird)، واستدعِ:swim()فقط إذا كان الطائر قادرًا على السباحة.
ستتلقى مُدخلين:
- اسم الطائر (سلسلة نصية)
- ما إذا كان الطائر قادرًا على السباحة:
yesأوno
إذا كان الطائر قادرًا على السباحة، فينبغي أن يكون الناتج:
{name} is eating
{name} chirps!
{name} is swimmingإذا لم يكن الطائر قادرًا على السباحة، فينبغي أن يكون الناتج:
{name} is eating
{name} chirps!على سبيل المثال، إذا كان المُدخلان هما Duck وyes، فينبغي أن يكون الناتج:
Duck is eating
Duck chirps!
Duck is swimmingإذا كان المُدخلان هما Sparrow وno، فينبغي أن يكون الناتج:
Sparrow is eating
Sparrow chirps!يوضّح هذا التحدي الفرق الأساسي: Bird هو Animal (وراثة)، لكن السباحة قدرة اختيارية (خلطة). تُطبَّق الخلطة على الفئة، لكن منطقك في الملف الرئيسي يقرّر ما إذا كان سيستخدمها استنادًا إلى قدرات الطائر الفردية!
جرّب بنفسك
-- main.lua: نقطة الدخول - إنشاء طائر وإظهار قدراته
-- دالة مساعدة لتطبيق mixin على class
local function applyMixin(class, mixin)
-- TODO: انسخ جميع الـ methods من الـ mixin إلى الـ class
end
-- طبّق الـ Swimmable mixin على Bird قبل require لـ Bird
-- Note: قد تحتاج إلى التعامل مع هذا في Bird.lua أو هنا
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- طبّق الـ mixin على class الـ Bird
applyMixin(Bird, Swimmable)
-- اقرأ المدخلات
local name = io.read()
local canSwimInput = io.read()
-- TODO: حدد ما إذا كان الطائر يستطيع السباحة بناءً على المدخل ("yes" أو "no")
-- TODO: أنشئ كائن Bird بالاسم وقدرة السباحة
-- TODO: استدعِ eat() - موروثة من Animal
-- TODO: استدعِ chirp() - method الخاصة بـ Bird
-- TODO: استدعِ swim() فقط إذا كان الطائر يستطيع السباحة
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
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 مقابل الوراثةملخص - تجميع الروبوتتدرّب بنفسك: مترجم Lua عبر الإنترنت