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

إعادة تعريف الدوال

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

حتى الآن، ورثت الفئات الفرعية دوال الفئات الأب وأضافت دوالاً جديدة. ولكن ماذا لو احتاجت الفئة الفرعية إلى نفس اسم الدالة بـ سلوك مختلف؟ يُسمى هذا بـ تجاوز الدوال (method overriding).

عندما تقوم بتعريف دالة (method) في فئة فرعية (child class) بنفس اسم دالة موجودة في الفئة الأب (parent class)، فإن نسخة الفئة الفرعية تكون لها الأولوية. تتحقق سلسلة البحث في Lua من الفئة الفرعية أولاً—فإذا وجدت الدالة هناك، فإنها تتوقف عن البحث.

local Animal = {}
Animal.__index = Animal

function Animal:new(name)
    local obj = {name = name}
    setmetatable(obj, Animal)
    return obj
end

function Animal:speak()
    print(self.name .. " makes a sound")
end

-- Dog يتجاوز speak()
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, {__index = Animal})

function Dog:new(name)
    local obj = Animal.new(Animal, name)
    setmetatable(obj, Dog)
    return obj
end

function Dog:speak()
    print(self.name .. " barks: Woof!")
end

الآن تستجيب كل فئة بشكل مختلف لنفس استدعاء الطريقة:

local generic = Animal:new("Creature")
local buddy = Dog:new("Buddy")

generic:speak()  -- المخرجات: Creature makes a sound
buddy:speak()    -- المخرجات: Buddy barks: Woof!

يستبدل :speak() الخاص بالكلب نسخة الأب تماماً بالنسبة لنماذج Dog. يتيح لك هذا إنشاء سلوك متخصص مع الحفاظ على واجهة متسقة—يمكن لكل حيوان أن :speak()، ولكن كل فصيل يفعل ذلك بشكل مختلف.

challenge icon

التحدي

سهل

لنقم ببناء مملكة حيوانات صغيرة حيث تتحدث المخلوقات المختلفة بطرقها الفريدة! ستقوم بإنشاء فئة أب Animal وفئتين ابنتين—Cat و Cow—تقوم كل منهما بتجاوز (override) تابع :speak() الخاص بالأب بسلوكها الخاص المتخصص.

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

  • Animal.lua: الفئة الأساسية الخاصة بك مع منشئ :new(name) يقوم بتخزين اسم الحيوان. قم بتضمين تابع :speak() يطبع اسم الحيوان متبوعاً بـ makes a sound. هذا هو السلوك الافتراضي الذي ستتجاوزه الفئات الابنة.
  • Cat.lua: فئة ابنة ترث من Animal. يجب أن يستدعي منشئها :new(name) منشئ الأب، ثم يعيد ربط الـ metatable بـ Cat. قم بتجاوز تابع :speak() بحيث تقول القطط شيئاً مختلفاً—اطبع اسم القطة متبوعاً بـ meows: Meow!
  • Cow.lua: فئة ابنة أخرى ترث من Animal. قم بإعداد المنشئ بنفس طريقة Cat. قم بتجاوز :speak() بحيث تطبع الأبقار اسمها متبوعاً بـ moos: Moo!
  • main.lua: اجمع كل شيء معاً! اقرأ اسمين من المدخلات، وأنشئ Cat بالاسم الأول و Cow بالاسم الثاني، ثم استدعِ :speak() على كل منهما لتوضيح أن نفس اسم التابع ينتج مخرجات مختلفة اعتماداً على الفئة.

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

  1. اسم القطة
  2. اسم البقرة

في ملفك الرئيسي، أنشئ كلا الحيوانين واستدعِ :speak() على القطة أولاً، ثم على البقرة.

على سبيل المثال، إذا كانت المدخلات هي Whiskers و Bessie، يجب أن تكون المخرجات:

Whiskers meows: Meow!
Bessie moos: Moo!

لاحظ كيف يستجيب كلا الحيوانين لنفس استدعاء تابع :speak()، ولكن كل منهما ينتج مخرجات خاصة بنوعه. نسخة Cat تستبدل تماماً سلوك الأب العام "makes a sound"، وكذلك تفعل نسخة Cow. هذا هو تجاوز التوابع (method overriding) في الواقع—نفس الواجهة، وتطبيقات مختلفة!

ورقة مرجعية

يسمح تجاوز الدوال (Method overriding) للفئة الابنة بتعريف دالة بنفس اسم دالة في الفئة الأب، مما يستبدل سلوك الأب بوظائف متخصصة.

عندما تبحث لغة Lua عن دالة، فإنها تتحقق من الفئة الابنة أولاً. إذا وجدت الدالة هناك، فإنها تستخدم هذا الإصدار وتتوقف عن البحث في الفئة الأب.

مثال أساسي على تجاوز الدوال:

local Animal = {}
Animal.__index = Animal

function Animal:new(name)
    local obj = {name = name}
    setmetatable(obj, Animal)
    return obj
end

function Animal:speak()
    print(self.name .. " makes a sound")
end

-- Dog overrides speak()
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, {__index = Animal})

function Dog:new(name)
    local obj = Animal.new(Animal, name)
    setmetatable(obj, Dog)
    return obj
end

function Dog:speak()
    print(self.name .. " barks: Woof!")
end

استخدام الدالة التي تم تجاوزها:

local generic = Animal:new("Creature")
local buddy = Dog:new("Buddy")

generic:speak()  -- Output: Creature makes a sound
buddy:speak()    -- Output: Buddy barks: Woof!

تستبدل دالة الفئة الابنة إصدار الفئة الأب تماماً بالنسبة لكائنات تلك الفئة الابنة، مما يسمح بسلوك متخصص مع الحفاظ على واجهة متسقة عبر جميع الفئات.

جرّب بنفسك

-- main.lua: اجمع كل شيء معاً

local Cat = require('Cat')
local Cow = require('Cow')

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

-- TODO: أنشئ قطة (Cat) بالاسم الأول

-- TODO: أنشئ بقرة (Cow) بالاسم الثاني

-- TODO: استدعِ الدالة :speak() على القطة أولاً، ثم على البقرة
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming