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

توسيع الـ Constructor

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

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

تتضمن التقنية استخدام اسم الفئة الأصلية لاستدعاء أسلوبها :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: أولًا، نستدعي Vehicle.new(Vehicle, speed) للحصول على كائن تم تعيين الحقل speed فيه مسبقًا. ثم نضيف الحقل الخاص بـ child، وهو brand. وأخيرًا، نغيّر metatable من Vehicle إلى Car بحيث يبحث instance عن methods في الفئة الابنة أولًا.

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

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

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

challenge icon

التحدي

سهل

لنَبْنِ تسلسلًا هرميًا للمخلوقات، حيث يوسّع صنف فرعي مُنشئ صنفه الأب! ستنشئ صنفًا أبًا هو Animal وصنفًا فرعيًا هو Bird، بحيث يستدعي مُنشئ Bird مُنشئ Animal لمعالجة التهيئة المشتركة قبل إضافة سِمته الفريدة.

ستنظّم التعليمات البرمجية عبر ثلاثة ملفات:

  • Animal.lua: عرّف صنفك الأب باستخدام مُنشئ :new(name) الذي يخزّن اسم الحيوان. أضف أسلوب :describe() يطبع اسم الحيوان. أعد إعداد نمط الصنف القياسي باستخدام __index وأعِد الصنف.
  • Bird.lua: عرّف صنفك الفرعي الذي يرث من Animal. يجب أن يستدعي مُنشئ Bird ذي الصيغة :new(name, wingspan) مُنشئ الصنف الأب باستخدام Animal.new(Animal, name) لتهيئة الاسم، ثم يضيف الحقل wingspan إلى الكائن، وأخيرًا يعيد ربط جدول التعريفات الوصفية بـ Bird. أضف أسلوب :info() يطبع الاسم وباع الجناحين بتنسيق محدد.
  • main.lua: اطلب وحدة Bird، واقرأ مُدخلين (الاسم وباع الجناحين)، وأنشئ كائنًا من 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 بالـ name و wingspan المعطيين

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

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

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

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

تدرّب بنفسك: مترجم Lua عبر الإنترنت