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

واجهة مشتركة

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

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

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

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

challenge icon

التحدي

سهل

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

ستنظّم code الخاص بك عبر ثلاثة files:

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

ستتلقى ثلاث inputs:

  1. رقم card ائتمانية (مثل: 4532-1234-5678)
  2. معرّف bank account (مثل: ACC-9876)
  3. مبلغ payment (مثل: 150)

في main file، أنشئ instance واحدة من كل نوع من payment باستخدام أول inputين. ثم استخدم function handlePayment لمعالجة المبلغ المعطى عبر كلتا payment methods: أولًا card الائتمانية، ثم التحويل المصرفي.

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

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

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

جرّب بنفسك

-- استورد فئات الدفع
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 عبر الإنترنت