Menu
CoddyTech

واجهة مشتركة

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

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

تخيّل أنك تبني نظامًا لتسجيل الأحداث. قد ترغب في إرسال الرسائل إلى وجهات مختلفة: ملف، أو الشاشة، أو حتى الشبكة. بدلًا من كتابة شيفرة منفصلة لكل وجهة، صمّم جميع فئات الإخراج لديك بحيث تنفّذ الطريقة نفسها :write():

local LogFile = {}
LogFile.__index = LogFile

function LogFile:new(filename)
    local obj = {filename = filename}
    setmetatable(obj, LogFile)
    return obj
end

function LogFile:write(message)
    print("[FILE:" .. self.filename .. "] " .. message)
end

local Screen = {}
Screen.__index = Screen

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

function Screen:write(message)
    print("[SCREEN] " .. message)
end

الآن يمكن لأي كود يحتاج إلى إخراج رسائل أن يقبل أيًّا من النوعين:

local function logMessage(output, msg)
    output:write(msg)
end

local file = LogFile:new("app.log")
local screen = Screen:new()

logMessage(file, "Starting up")    -- [FILE:app.log] جارٍ بدء التشغيل
logMessage(screen, "Starting up")  -- [SCREEN] جارٍ بدء التشغيل

الدالة logMessage لا تعرف الفئة التي تتلقاها ولا تهتم بها. إنها تتوقع فقط وجود طريقة :write(). وهذا يجعل شفرتك مرنة. يمكنك إضافة أنواع إخراج جديدة لاحقًا دون تغيير الدوال الحالية.

challenge icon

التحدي

سهل

لنُنشئ نظامًا لمعالجة المدفوعات يتيح استخدام وسائل دفع مختلفة بالتبادل! ستنشئ صنفين للدفع لا تربطهما أي علاقة، لكنهما يشتركان في واجهة مشتركة، وهي الطريقة :process(amount)، مما يسمح لأي شفرة تتعامل مع المدفوعات بالعمل مع أيٍّ من النوعين دون معرفة الفرق بينهما.

ستنظّم شفرتك في ثلاثة ملفات:

  • CreditCard.lua: صنف للدفع يحتوي على مُنشئ :new(cardNumber) يخزّن رقم البطاقة. يجب أن تطبع طريقته :process(amount) النص Charging {amount} to card {cardNumber}.
  • BankTransfer.lua: صنف دفع منفصل تمامًا يحتوي على مُنشئ :new(accountId). يجب أن تطبع طريقته :process(amount) النص Transferring {amount} from account {accountId}.
  • main.lua: أنشئ دالة باسم handlePayment(paymentMethod, amount) تقبل أي كائن دفع ومبلغًا، ثم تستدعي عليه :process(amount). لا تحتاج هذه الدالة إلى معرفة ما إذا كانت تتعامل مع بطاقة ائتمان أم تحويل بنكي. كل ما تتوقعه هو أن يكون للكائن طريقة :process().

ستتلقى ثلاثة مُدخلات:

  1. رقم بطاقة ائتمان (مثل 4532-1234-5678)
  2. معرّف حساب بنكي (مثل ACC-9876)
  3. مبلغ الدفع (مثل 150)

في ملفك الرئيسي، أنشئ نسخة واحدة من كل نوع دفع باستخدام أول مُدخلين. ثم استخدم دالتك handlePayment لمعالجة المبلغ المُعطى عبر طريقتَي الدفع كلتيهما: بطاقة الائتمان أولًا، ثم التحويل البنكي.

على سبيل المثال، إذا كانت المُدخلات هي 1111-2222-3333 وACC-5555 و75، فيجب أن يكون الناتج:

Charging 75 to card 1111-2222-3333
Transferring 75 from account ACC-5555

يكمن جمال هذا التصميم في أن دالتك handlePayment تعمل مع أي كائن يطبّق :process(). يمكنك إضافة صنف PayPal غدًا، وما دام لديه طريقة :process(amount)، فسيعمل مع دالتك الحالية دون أي تغييرات!

جرّب بنفسك

-- استورد أصناف الدفع
local CreditCard = require('CreditCard')
local BankTransfer = require('BankTransfer')

-- اقرأ المدخلات
local cardNumber = io.read()
local accountId = io.read()
local amount = tonumber(io.read())

-- TODO: أنشئ الدالة handlePayment التي تقبل أي كائن دفع
-- ومبلغًا، ثم تستدعي عليه :process(amount)


-- TODO: أنشئ كائنين من CreditCard وBankTransfer باستخدام المدخلات


-- TODO: استخدم handlePayment لمعالجة المبلغ عبر طريقتَي الدفع
-- البطاقة الائتمانية أولًا، ثم التحويل المصرفي
quiz iconاختبر نفسك

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

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

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