Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

Mixins مقابل الوراثة

جزء من قسم Object Oriented Programming في رحلة Lua على Coddy — الدرس 53 من 70.

لقد تعلمت طريقتين لمشاركة السلوك: الوراثة (inheritance) تنشئ علاقة "is-a" من خلال سلسلة النماذج الأولية (prototype chain)، بينما تقوم الـ mixins بنسخ الدوال مباشرة إلى الفئة (class). ولكن متى يجب عليك استخدام كل نهج؟

استخدم الوراثة عندما تتشارك الكائنات هوية أساسية. فالطائر Bird هو حيوان Animal—ومن المنطقي أن يرث Bird سلوكيات الحيوان الأساسية مثل :eat() أو :sleep(). هذه العلاقة دائمة وتحدد ماهية الكائن بشكل أساسي.

استخدم mixins عندما تريد إضافة قدرة لا تُعرّف الهوية الأساسية للكائن.

ليست كل الطيور قادرة على الطيران—فالبطاريق والنعام طيور، لكنها لا تطير. الطيران هو قدرة، وليس سمة محددة لكون الكائن طائراً. تسمح لك الـ mixin المسماة Flyable بإضافة ميزة الطيران للطيور التي تحتاجها، دون فرضها على جميع الطيور.

يعمل النهجان معًا بشكل رائع:

-- يرث 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)

يمنحك هذا النمط أفضل ما في العالمين: تسلسل هرمي واضح للفئات من أجل الهوية المشتركة، بالإضافة إلى قدرات مرنة يمكن دمجها عند الحاجة.

challenge icon

التحدي

سهل

لنقم ببناء نظام بيئي صغير من الكائنات يوضح متى نستخدم الوراثة (inheritance) مقابل الخلطات (mixins)! ستقوم بإنشاء فئة أساسية Animal، وفئة Bird ترث منها، وخلطة Swimmable—لأنه بينما كل الطيور حيوانات، لا تستطيع كل الطيور السباحة.

ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:

  • Animal.lua: أنشئ فئة Animal الأساسية مع منشئ :new(name) يقوم بتخزين الاسم. قم بتضمين تابع :eat() يطبع {name} is eating. يمثل هذا الهوية الجوهرية التي تشترك فيها جميع الحيوانات.
  • Swimmable.lua: أنشئ خلطة (mixin) مع تابع :swim() يطبع {self.name} is swimming. هذه قدرة—ليست كل الكائنات تستطيع السباحة، لذا لا ينبغي أن تكون جزءاً من سلسلة الوراثة.
  • Bird.lua: أنشئ فئة Bird ترث من Animal. يجب أن يستدعي المنشئ :new(name, canSwim) منشئ الفئة الأب ويخزن ما إذا كان هذا الطائر يستطيع السباحة أم لا. قم بتطبيق خلطة Swimmable على فئة Bird. قم بتضمين تابع :chirp() يطبع {name} chirps!—هذا سلوك خاص بالطيور.
  • main.lua: اكتب الدالة المساعدة applyMixin هنا. اقرأ المدخلات لاسم الطائر وما إذا كان بإمكانه السباحة (yes أو no). أنشئ مثيلاً (instance) من Bird، ثم استعرض قدراته: استدعِ :eat() (الموروث من Animal)، واستدعِ :chirp() (تابع خاص بـ Bird)، واستدعِ :swim() فقط إذا كان الطائر يستطيع السباحة.

ستتلقى مدخلين:

  1. اسم الطائر (سلسلة نصية)
  2. ما إذا كان الطائر يستطيع السباحة: 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!

يوضح هذا التحدي التمييز الرئيسي: الطائر هو حيوان (وراثة)، لكن السباحة هي قدرة اختيارية (mixin). يتم تطبيق الخلطة على الفئة، لكن منطقك الأساسي يقرر ما إذا كان سيستخدمها بناءً على قدرات الطائر الفردي!

ورقة مرجعية

استخدم الوراثة (inheritance) عندما تتشارك الكائنات هوية أساسية (علاقة "هو-يكون"). استخدم الـ mixins عند إضافة قدرات لا تحدد الهوية الجوهرية للكائن.

تنشئ الوراثة علاقة دائمة عبر سلسلة النماذج الأولية (prototype chain):

local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})

تضيف الـ mixins قدرات اختيارية يمكن تطبيقها بشكل انتقائي:

local Flyable = {}
function Flyable:fly()
    print(self.name .. " soars through the sky!")
end

applyMixin(Bird, Flyable)

يعمل النهجان معاً: استخدم الوراثة للهوية المشتركة والـ mixins للقدرات المرنة التي تحتاجها بعض الحالات فقط.

جرّب بنفسك

-- main.lua: نقطة الدخول - إنشاء طائر واستعراض قدراته

-- دالة مساعدة لتطبيق mixin على فئة (class)
local function applyMixin(class, mixin)
    -- TODO: نسخ جميع الأساليب (methods) من mixin إلى الفئة
    
end

-- تطبيق Swimmable mixin على Bird قبل استدعاء Bird
-- ملاحظة: قد تحتاج إلى معالجة هذا في Bird.lua أو هنا

local Bird = require('Bird')
local Swimmable = require('Swimmable')

-- تطبيق mixin على فئة Bird
applyMixin(Bird, Swimmable)

-- قراءة المدخلات
local name = io.read()
local canSwimInput = io.read()

-- TODO: تحديد ما إذا كان الطائر يمكنه السباحة بناءً على المدخلات ("yes" أو "no")

-- TODO: إنشاء مثيل (instance) من Bird بالاسم والقدرة على السباحة

-- TODO: استدعاء eat() - موروثة من Animal

-- TODO: استدعاء chirp() - أسلوب خاص بـ Bird

-- TODO: استدعاء swim() فقط إذا كان الطائر يمكنه السباحة
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming