Menu
CoddyTech

Wspólny interfejs

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

Duck typing pokazał nam, że niezwiązane ze sobą klasy mogą współpracować, jeśli mają metody o takich samych nazwach. Ta idea prowadzi do potężnej koncepcji projektowej: wspólnego interfejsu. 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: do pliku, na ekran, a nawet przez sieć. 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 interesuje się tym, jaką klasę otrzymuje. Oczekuje jedynie 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

Stwórzmy 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, które mają wspólny interfejs — metodę :process(amount). Dzięki temu każdy kod obsługujący płatności będzie działać z dowolną z tych klas, nie wiedząc, czym się różnią.

Rozmieścisz 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 niezależna 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 obsługuje kartę kredytową, czy przelew bankowy. Wystarczy, że obiekt ma metodę :process().

Otrzymasz trzy dane wejściowe:

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

W pliku głównym 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ę przy użyciu obu metod płatności: najpierw karty kredytowej, a potem przelewu bankowego.

Na przykład, jeśli dane wejściowe to 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 projektu jest to, że funkcja handlePayment działa z każdym obiektem implementującym :process(). Możesz jutro dodać klasę PayPal i o ile będzie miała metodę :process(amount), zadział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