Menu
Coddy logo textTech

Wspólny interfejs

Część sekcji Programowanie obiektowe ścieżki Lua w Coddy. Lekcja 40 z 70.

Duck typing pokazało nam, że niezwiązane ze sobą klasy mogą współpracować, jeśli mają metody o tych samych nazwach. Ta idea prowadzi do potężnej koncepcji projektowej: wspólny interfejs. Gdy celowo projektujesz wiele klas tak, aby miały takie same sygnatury metod, tworzysz wymienne komponenty.

Wyobraź sobie, że tworzysz system rejestrowania. Możesz chcieć wysyłać komunikaty do różnych miejsc docelowych: do pliku, na ekran, a nawet do sieci. Zamiast pisać osobny kod dla każdego z nich, projektujesz wszystkie klasy wyjściowe tak, aby implementowały tę samą metodę :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

Teraz każdy kod, który musi wyświetlać komunikaty, może akceptować oba typy:

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

Funkcja logMessage nie wie ani nie obchodzi jej, jaką klasę otrzymuje. Oczekuje tylko metody :write(). Dzięki temu Twój kod jest elastyczny. Możesz później dodać nowe typy wyjścia bez zmieniania istniejących funkcji.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system przetwarzania płatności, w którym można używać różnych metod płatności zamiennie! Utworzysz dwie całkowicie niezależne klasy płatności ze wspólnym interfejsem — metodą :process(amount), dzięki czemu każdy kod obsługujący płatności będzie działał z dowolną z nich, nie wiedząc, z którą ma do czynienia.

Rozmieścisz swój kod w trzech plikach:

  • CreditCard.lua: Klasa płatności z konstruktorem :new(cardNumber), który przechowuje numer karty. Jej metoda :process(amount) powinna wypisywać Charging {amount} to card {cardNumber}.
  • BankTransfer.lua: Całkowicie oddzielna klasa płatności z konstruktorem :new(accountId). Jej metoda :process(amount) powinna wypisywać Transferring {amount} from account {accountId}.
  • main.lua: Utwórz funkcję o nazwie handlePayment(paymentMethod, amount), która przyjmuje dowolny obiekt płatności i kwotę, a następnie wywołuje na nim :process(amount). Ta funkcja nie musi wiedzieć, czy ma do czynienia z kartą kredytową, czy przelewem bankowym. Oczekuje jedynie, że obiekt będzie mieć metodę :process().

Otrzymasz trzy dane wejściowe:

  1. Numer karty kredytowej (np. 4532-1234-5678)
  2. Identyfikator konta bankowego (np. ACC-9876)
  3. Kwotę płatności (np. 150)

W głównym pliku utwórz po jednej instancji każdego typu płatności, używając dwóch pierwszych danych wejściowych. Następnie użyj funkcji handlePayment, aby przetworzyć podaną kwotę za pomocą obu metod płatności: najpierw karty kredytowej, a potem przelewu bankowego.

Na przykład, jeśli danymi wejściowymi są 1111-2222-3333, ACC-5555 i 75, wynikiem powinno być:

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

Zaletą tego rozwiązania jest to, że funkcja handlePayment działa z każdym obiektem implementującym :process(). Jutro możesz dodać klasę PayPal, a jeśli będzie mieć metodę :process(amount), będzie działać z istniejącą funkcją bez żadnych zmian!

Spróbuj swoich sił

-- Zaimportuj klasy płatności
local CreditCard = require('CreditCard')
local BankTransfer = require('BankTransfer')

-- Odczytaj dane wejściowe
local cardNumber = io.read()
local accountId = io.read()
local amount = tonumber(io.read())

-- TODO: Utwórz funkcję handlePayment, która przyjmuje dowolny obiekt płatności
-- oraz kwotę, a następnie wywołuje na nim :process(amount)


-- TODO: Utwórz instancje CreditCard i BankTransfer na podstawie danych wejściowych


-- TODO: Użyj handlePayment, aby przetworzyć kwotę obiema metodami płatności
-- Najpierw kartą kredytową, a potem przelewem bankowym
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Lua online