إعادة تعريف الدوال
جزء من قسم 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()، ولكن كل فصيل يفعل ذلك بشكل مختلف.
التحدي
سهللنقم ببناء مملكة حيوانات صغيرة حيث تتحدث المخلوقات المختلفة بطرقها الفريدة! ستقوم بإنشاء فئة أب 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()على كل منهما لتوضيح أن نفس اسم التابع ينتج مخرجات مختلفة اعتماداً على الفئة.
ستتلقى مدخلين:
- اسم القطة
- اسم البقرة
في ملفك الرئيسي، أنشئ كلا الحيوانين واستدعِ :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() على القطة أولاً، ثم على البقرة
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس 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 للابنالمشترك مقابل الفريدملخص - التسلسل الهرمي للأشكال