إعادة التعريف وتعدد الأشكال
أعد تعريف أساليب الفئة الأصلية وحقّق الاستدعاء متعدد الأشكال وقت التشغيل
إعادة التعريف وتعدد الأشكال درس مجاني في Lua Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Lua Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Lua Academy 4 دروس في المجموع.
تجاوز الدوال
يتجاوز الصنف الابن دالةً في الصنف الأب عبر تعريف دالة تحمل الاسم نفسه. وعند استدعائها على مثيل من الصنف الابن، تُنفّذ نسخة الابن. ولا تزال نسخة الأب متاحة عبر Parent.method(self).
local Shape={} Shape.__index=Shape
function Shape:area() return 0 end
function Shape:describe() print(type(self).." area="..self:area()) end
local Circle={} Circle.__index=Circle
setmetatable(Circle,{__index=Shape})
function Circle.new(r) return setmetatable({r=r},Circle) end
function Circle:area() return math.pi*self.r^2 end
local Square={} Square.__index=Square
setmetatable(Square,{__index=Shape})
function Square.new(s) return setmetatable({s=s},Square) end
function Square:area() return self.s^2 end
Circle.new(5):describe()
Square.new(4):describe()التوجيه متعدد الأشكال
تعدد الأشكال يعني أن أنواعًا مختلفة من الكائنات تستجيب للرسالة نفسها بطرق مختلفة. فالدالة التي تقبل أي «شكل» وتستدعي :area() تعمل مع أي نوع من الأشكال، إذ يستجيب كل نوع بتطبيقه الخاص.
local shapes={
Circle.new(3),
Square.new(4),
Circle.new(1),
}
local total=0
for _,shape in ipairs(shapes) do
total=total+shape:area()
end
print(string.format("Total area: %.2f",total))الكتابة بأسلوب البطة
تستخدم Lua الكتابة بأسلوب البطة: إذا كان الكائن يملك الدالة، فإنه يعمل. ولا حاجة إلى إعلان واجهة رسمي. فالدالة التي تستدعي :speak() تعمل مع أي كائن يملك دالة speak، بصرف النظر عن صنفه.
local function makeNoise(obj)
if type(obj.speak)=="function" then
obj:speak()
else
print("(silent)")
end
end
local Duck={speak=function(self) print("Quack!") end}
local Robot={speak=function(self) print("Beep boop!") end}
local Rock={}
makeNoise(Duck) -- Quack!
makeNoise(Robot) -- Beep boop!
makeNoise(Rock) -- (silent)تعدد الأشكال في المعاملات
تتيح الدوال الوصفية مثل __add تعدد الأشكال في المعاملات. ويمكن للأنواع المختلفة الاستجابة للمعامل + بدلالاتها الخاصة ما دامت تعرّف __add.
local Vec2={} Vec2.__index=Vec2
Vec2.__add=function(a,b) return Vec2.new(a.x+b.x,a.y+b.y) end
Vec2.__tostring=function(v) return "("..v.x..","..v.y..")" end
function Vec2.new(x,y) return setmetatable({x=x,y=y},Vec2) end
local Color={} Color.__index=Color
Color.__add=function(a,b)
return Color.new(math.min(255,a.r+b.r),math.min(255,a.g+b.g),math.min(255,a.b+b.b))
end
function Color.new(r,g,b) return setmetatable({r=r,g=g,b=b},Color) end
print(tostring(Vec2.new(1,2)+Vec2.new(3,4))) -- (4,6)نمط الزائر
يفصل نمط الزائر العمليات عن الكائنات. «تزور» العملية كائنات من أنواع مختلفة، وتستدعي الدالة المناسبة بناءً على النوع. استخدمه للعمليات التي يختلف سلوكها حسب النوع، من دون تعديل الأنواع نفسها.
local function printInfo(obj)
local mt = getmetatable(obj)
if mt == Circle then
print(string.format("Circle r=%.1f area=%.2f",obj.r,obj:area()))
elseif mt == Square then
print(string.format("Square s=%.1f area=%.2f",obj.s,obj:area()))
end
end
for _,s in ipairs({Circle.new(3),Square.new(4)}) do
printInfo(s)
endجدول الواجهة
عرّف الواجهة على أنها جدول لأسماء الدوال. وتحقّق من أن الكائن ينفّذ جميع الدوال المطلوبة قبل استخدامه بأسلوب تعدد الأشكال.
local function implements(obj, interface)
for _, method in ipairs(interface) do
if type(obj[method]) ~= "function" then
return false, "missing: "..method
end
end
return true
end
local Drawable = {"draw","resize","move"}
local Widget = {
draw=function(self) print("draw "..self.type) end,
resize=function(self,w,h) self.w,self.h=w,h end,
move=function(self,x,y) self.x,self.y=x,y end,
type="button"
}
local ok,err = implements(Widget,Drawable)
print(ok,err) -- true nilالتحقق من وجود الدالة
قبل استدعاء دالة اختيارية بأسلوب تعدد الأشكال، تحقّق من أن الكائن يملكها. يتيح ذلك إمكانات اختيارية من دون اشتراط وجود صنف أساسي مشترك.
local function maybeUpdate(obj, dt)
if type(obj.update)=="function" then
obj:update(dt)
end
end
local objects={
{x=0,y=0,update=function(self,dt) self.x=self.x+10*dt end},
{x=0,y=0}, -- no update
{x=0,y=0,update=function(self,dt) self.y=self.y+5*dt end},
}
for _,o in ipairs(objects) do maybeUpdate(o,1) end
for _,o in ipairs(objects) do print(o.x,o.y) endجدول التوجيه حسب النوع
بدلًا من سلاسل if/elseif، استخدم جدول توجيه: اربط أسماء الأنواع بدوال المعالجة. يُعرف هذا بنمط تعدد الأشكال المعتمد على الجداول.
local handlers={
number = function(v) return "num:"..v end,
string = function(v) return "str:\"".."..v.."\"" end,
boolean = function(v) return "bool:"..(v and "T" or "F") end,
table = function(v) return "tbl:#"..tostring(#v) end,
}
local function describe(v)
local h=handlers[type(v)]
return h and h(v) or "unknown"
end
print(describe(42))
print(describe("hello"))
print(describe({1,2,3}))القيم المعادة المتغايرة
يمكن لدوال الصنف الابن إعادة أنواع أكثر تحديدًا من دوال الصنف الأب، ويُسمّى ذلك الإعادة المتغايرة. ونظرًا إلى أن Lua ذات كتابة ديناميكية، فهذا أمر طبيعي؛ إذ يعيد مُنشئ الصنف الابن مثيلًا من الصنف الابن، لا من الأب فقط.
local Base={} Base.__index=Base
function Base.new() return setmetatable({type="base"},Base) end
function Base:copy() return Base.new() end
local Child={} Child.__index=Child
setmetatable(Child,{__index=Base})
function Child.new(extra)
local self=setmetatable({type="child"},Child)
self.extra=extra; return self
end
function Child:copy() return Child.new(self.extra) end -- covariant
local c=Child.new("hello")
local c2=c:copy()
print(c2.extra,getmetatable(c2)==Child) -- hello trueملخص تعدد الأشكال
يظهر تعدد الأشكال في Lua عمليًا في استخدام اسم الدالة نفسه مع سلوك مختلف لكل نوع. ويتحقق ذلك عبر البرمجة كائنية التوجه المعتمدة على metatable، أو فحوصات الكتابة بأسلوب البطة، أو جداول التوجيه. ولا حاجة إلى نظام واجهات رسمي، بل إلى اصطلاحات متسقة لتسمية الدوال.
-- Three ways to achieve polymorphism:
-- 1. Metatable inheritance + method override
-- 2. Duck typing: just call it if it exists
-- 3. Dispatch table: map type -> handler
local function area(shape)
-- Duck typing approach:
assert(type(shape.area)=="function","needs area()")
return shape:area()
end
print(area({area=function() return 16 end})) -- 16سؤال حول تعدد الأشكال
كيف يعمل تعدد الأشكال في Lua؟
مراجعة: التجاوز وتعدد الأشكال
تجاوز دوال الصنف الأب بإعادة تعريفها في الصنف الابن. وتحل Lua الاستدعاءات من الأسفل إلى الأعلى عبر سلسلة __index، فتحقق توجيهًا متعدد الأشكال أثناء التشغيل بصورة طبيعية.
الأسئلة الشائعة
هل درس «إعادة التعريف وتعدد الأشكال» مجاني؟
نعم — نص درس «إعادة التعريف وتعدد الأشكال» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Lua Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Lua Academy 4 دروس في المجموع.
ماذا ستتعلم في «إعادة التعريف وتعدد الأشكال»؟
أعد تعريف أساليب الفئة الأصلية وحقّق الاستدعاء متعدد الأشكال وقت التشغيل تتمرن على Lua Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Lua Academy؟
لا تُشترط خبرة سابقة. Lua Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «إعادة التعريف وتعدد الأشكال»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Lua Academy هذا؟
نعم. كل درس في Lua Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- الوراثة الأحادية مع تسلسل __index
- استدعاء أساليب الفئة الأصلية باستخدام super
- أنماط Mixin
- إعادة التعريف وتعدد الأشكال