واجهة مشتركة
جزء من قسم البرمجة كائنية التوجه في رحلة 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(). وهذا يجعل شفرتك مرنة. يمكنك إضافة أنواع إخراج جديدة لاحقًا دون تغيير الدوال الحالية.
التحدي
سهللنُنشئ نظامًا لمعالجة المدفوعات يتيح استخدام وسائل دفع مختلفة بالتبادل! ستنشئ صنفين للدفع لا تربطهما أي علاقة، لكنهما يشتركان في واجهة مشتركة، وهي الطريقة :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().
ستتلقى ثلاثة مُدخلات:
- رقم بطاقة ائتمان (مثل
4532-1234-5678) - معرّف حساب بنكي (مثل
ACC-9876) - مبلغ الدفع (مثل
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 لمعالجة المبلغ عبر طريقتَي الدفع
-- البطاقة الائتمانية أولًا، ثم التحويل المصرفي
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
4مشروع: بنك رقمي
إعداد المشروعطريقة الإيداع7تعدد الأشكال وإعادة التعريف
إعادة تعريف الدوالاستدعاء دوال الفئة الأبDuck Typingواجهة مشتركةالتحقق من النوعمراجعة - أدوار الموظفين2نمط النموذج الأولي للفئات
مفهوم النموذج الأوليالربط باستخدام __indexالمنشئ :new()تهيئة الخصائصنسخ مستقلةملخص - مصنع السيارات5تحميل المعاملات في OOP
جمع الكائناتطرح الكائناتدمج الكائناتمقارنة الكائنات (<, >)مراجعة - رياضيات المحفظة8التغليف
اصطلاحات التسميةاستخدام Closures للخصوصيةالوصول عبر Closuresجداول للقراءة فقطمنطق التحقق من الصحةمراجعة - الخزنة الآمنة11أنماط التصميم (مبسط)
دوال المصنعجدول Singletonنمط الـ Iteratorالمراقب (المستمع)مراجعة - مصنع الـ Logger3حالة الكائن وسلوكه
متغيرات النسخةدالات الجلب (Getter Methods)دالات التعيين (Setter Methods)الخصائص المحسوبةتنسيق النصوصالتحقق من المساواةمراجعة - درجة الطالب6أساسيات الوراثة
إعداد الوراثةوراثة الـ Methodsتوسيع الـ Constructorإضافة Methods للابنالمشترك مقابل الفريدملخص - التسلسل الهرمي للأشكالتدرّب بنفسك: مترجم Lua عبر الإنترنت