Menu
CoddyTech

Interfaccia comune

Fa parte della sezione Programmazione orientata agli oggetti del percorso Lua di Coddy. Lezione 40 di 70.

Il duck typing ci ha mostrato che classi non correlate possono lavorare insieme se condividono i nomi dei metodi. Questa idea porta a un potente concetto di progettazione: l’interfaccia comune. Quando progetti intenzionalmente più classi affinché abbiano le stesse firme dei metodi, crei componenti intercambiabili.

Immagina di creare un sistema di registrazione. Potresti voler inviare messaggi a destinazioni diverse: un file, lo schermo o persino una rete. Invece di scrivere codice separato per ciascuna, progetti tutte le tue classi di output in modo che implementino lo stesso metodo :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

Ora qualsiasi codice che deve produrre messaggi può accettare entrambi i tipi:

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] Avvio in corso
logMessage(screen, "Starting up")  -- [SCREEN] Avvio in corso

La funzione logMessage non sa né le interessa quale classe riceve. Si aspetta solo un metodo :write(). Questo rende il tuo codice flessibile. Puoi aggiungere nuovi tipi di output in seguito senza modificare le funzioni esistenti.

challenge icon

Sfida

Facile

Costruiamo un sistema di elaborazione dei pagamenti in cui si possano usare in modo intercambiabile diversi metodi di pagamento! Creerai due classi di pagamento completamente indipendenti che condividono un'interfaccia comune, un metodo :process(amount), consentendo a qualsiasi codice che gestisce i pagamenti di funzionare con entrambi i tipi senza distinguerli.

Organizzerai il codice in tre file:

  • CreditCard.lua: una classe di pagamento con un costruttore :new(cardNumber) che memorizza il numero della carta. Il suo metodo :process(amount) dovrebbe stampare Charging {amount} to card {cardNumber}.
  • BankTransfer.lua: una classe di pagamento completamente separata con un costruttore :new(accountId). Il suo metodo :process(amount) dovrebbe stampare Transferring {amount} from account {accountId}.
  • main.lua: crea una funzione chiamata handlePayment(paymentMethod, amount) che accetta un qualsiasi oggetto di pagamento e un importo, quindi chiama :process(amount) su di esso. Questa funzione non deve sapere se sta gestendo una carta di credito o un bonifico bancario. Si aspetta solo che l'oggetto abbia un metodo :process().

Riceverai tre input:

  1. Un numero di carta di credito (ad es., 4532-1234-5678)
  2. Un ID di conto bancario (ad es., ACC-9876)
  3. Un importo di pagamento (ad es., 150)

Nel file principale, crea un'istanza di ciascun tipo di pagamento usando i primi due input. Poi usa la funzione handlePayment per elaborare l'importo indicato con entrambi i metodi di pagamento: prima la carta di credito, poi il bonifico bancario.

Ad esempio, se gli input sono 1111-2222-3333, ACC-5555 e 75, l'output dovrebbe essere:

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

Il bello di questo design è che la funzione handlePayment funziona con qualsiasi oggetto che implementi :process(). Domani potresti aggiungere una classe PayPal e, purché abbia un metodo :process(amount), funzionerebbe con la funzione esistente senza alcuna modifica!

Provalo tu

-- Importa le classi di pagamento
local CreditCard = require('CreditCard')
local BankTransfer = require('BankTransfer')

-- Leggi i dati di input
local cardNumber = io.read()
local accountId = io.read()
local amount = tonumber(io.read())

-- TODO: Crea la funzione handlePayment che accetta qualsiasi oggetto di pagamento
-- e un importo, poi chiama :process(amount) sull'oggetto


-- TODO: Crea istanze di CreditCard e BankTransfer usando i dati di input


-- TODO: Usa handlePayment per elaborare l'importo con entrambi i metodi di pagamento
-- Prima con la carta di credito, poi con il bonifico bancario
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore Lua online