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