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 אונליין