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

توسيع الـ Constructor

جزء من قسم البرمجة كائنية التوجه في رحلة Lua على Coddy — الدرس 33 من 70.

عندما ترث فئة تابعة (child class) من فئة أساسية (parent class)، فإنك غالباً ما ترغب في أن يقوم منشئ الفئة التابعة (constructor) بتهيئة كل ما تقوم الفئة الأساسية بتهيئته، بالإضافة إلى أي حقول إضافية خاصة بالفئة التابعة. وبدلاً من تكرار الكود، يمكنك استدعاء منشئ الفئة الأساسية مباشرةً.

تتضمن هذه التقنية استخدام اسم الفئة الأب (parent class) لاستدعاء التابع :new() الخاص بها، مع تمرير self كمعامل أول. يؤدي هذا إلى إنشاء الكائن الأساسي مع كافة عمليات تهيئة الأب، والتي تقوم بعد ذلك بتخصيصها للابن.

local Vehicle = {}
Vehicle.__index = Vehicle

function Vehicle:new(speed)
    local obj = {speed = speed}
    setmetatable(obj, Vehicle)
    return obj
end

-- Car يرث من Vehicle
local Car = {}
Car.__index = Car
setmetatable(Car, {__index = Vehicle})

function Car:new(speed, brand)
    local obj = Vehicle.new(Vehicle, speed)  -- استدعاء منشئ الأب
    obj.brand = brand                         -- إضافة حقل خاص بالابن
    setmetatable(obj, Car)                    -- إعادة الربط بـ Car
    return obj
end

لاحظ الخطوات الرئيسية في منشئ الفئة الفرعية (child constructor): أولاً، نقوم باستدعاء Vehicle.new(Vehicle, speed) للحصول على كائن يحتوي على حقل speed معد مسبقاً. ثم نقوم بإضافة حقل brand الخاص بالفئة الفرعية. أخيراً، نقوم بتغيير الجدول الوصفي (metatable) من Vehicle إلى Car حتى يبحث الكائن عن الدوال في الفئة الفرعية أولاً.

يمكنك أيضًا استخدام صيغة النقطتين الرأسيتين عند استدعاء منشئ الفئة الأب — Vehicle:new(speed) تعادل Vehicle.new(Vehicle, speed)، بما أن كليهما يمرر Vehicle كوسيط أول. كلا الشكلين يعملان بشكل صحيح هنا، لذا لا تتردد في استخدام أيهما تجده أكثر قابلية للقراءة.

local myCar = Car:new(120, "Toyota")
print(myCar.speed)  -- المخرجات: 120
print(myCar.brand)  -- المخرجات: Toyota

هذا النمط يحافظ على كودك بنظام DRY—حيث يقع منطق التهيئة المشترك في الفئة الأب (parent)، بينما يتعامل كل تابع (child) فقط مع سماته الفريدة.

challenge icon

التحدي

سهل

لنقم ببناء تسلسل هرمي للكائنات حيث تمدد الفئة الابنة (child class) منشئ الفئة الأب (parent class)! ستقوم بإنشاء فئة أب باسم Animal وفئة ابنة باسم Bird، حيث يستدعي منشئ Bird منشئ Animal للتعامل مع التهيئة المشتركة قبل إضافة سمته الفريدة الخاصة.

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

  • Animal.lua: قم بتعريف الفئة الأب باستخدام منشئ :new(name) الذي يخزن اسم الحيوان. قم بتضمين تابع :describe() يطبع اسم الحيوان. قم بإعداد نمط الفئة القياسي باستخدام __index وأعد الفئة.
  • Bird.lua: قم بتعريف الفئة الابنة التي ترث من Animal. يجب أن يستدعي منشئ :new(name, wingspan) الخاص بـ Bird منشئ الأب باستخدام Animal.new(Animal, name) لتهيئة الاسم، ثم إضافة حقل wingspan إلى الكائن، وأخيراً إعادة ربط الـ metatable بـ Bird. قم بتضمين تابع :info() يطبع كلاً من الاسم وطول جناحي الطائر بتنسيق محدد.
  • main.lua: اطلب وحدة Bird، واقرأ مدخلين (الاسم وطول الجناحين)، وأنشئ مثيلاً (instance) من Bird، واستدعِ كلا التابعين لإثبات أن الوظائف الموروثة والخاصة بالابن تعمل معاً.

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

  1. اسم الطائر
  2. طول جناحي الطائر (رقم)

في ملفك الرئيسي، أنشئ Bird بالاسم وطول الجناحين المعطيين. ثم استدعِ :describe() متبوعاً بـ :info() في أسطر منفصلة.

يجب أن يطبع التابع :describe() (الموروث من Animal):

Animal: {name}

يجب أن يطبع التابع :info() (المعرف في Bird):

{name} has a wingspan of {wingspan}

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

Animal: Eagle
Eagle has a wingspan of 200

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

جرّب بنفسك

-- main.lua: نقطة الدخول

local Bird = require('Bird')

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

-- TODO: إنشاء كائن Bird بالاسم و wingspan المعطيين

-- TODO: استدعاء :describe() على الطائر (موروثة من Animal)

-- TODO: استدعاء :info() على الطائر (معرفة في Bird)
quiz iconاختبر نفسك

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

جميع دروس البرمجة كائنية التوجه