Menu
Coddy logo textTech

Gemeinsames Interface

Teil des Abschnitts Objektorientierte Programmierung der Lua-Journey von Coddy. Lektion 40 von 70.

Duck-Typing hat uns gezeigt, dass nicht verwandte Klassen zusammenarbeiten können, wenn sie Methodennamen gemeinsam haben. Diese Idee führt zu einem leistungsstarken Designkonzept: der gemeinsamen Schnittstelle. Wenn du mehrere Klassen bewusst so entwirfst, dass sie dieselben Methodensignaturen haben, erstellst du austauschbare Komponenten.

Stell dir vor, du baust ein Protokollierungssystem. Vielleicht möchtest du Nachrichten an verschiedene Ziele senden: eine Datei, den Bildschirm oder sogar ein Netzwerk. Anstatt für jedes Ziel separaten Code zu schreiben, entwirfst du alle deine Ausgabe-Klassen so, dass sie dieselbe :write()-Methode implementieren:

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

Jetzt kann jeder Code, der Nachrichten ausgeben muss, entweder einen der beiden Typen akzeptieren:

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

Die Funktion logMessage weiß nicht und kümmert sich nicht darum, welche Klasse sie erhält. Sie erwartet lediglich eine :write()-Methode. Dadurch wird dein Code flexibel. Du kannst später neue Ausgabetypen hinzufügen, ohne bestehende Funktionen zu ändern.

challenge icon

Aufgabe

Einfach

Wir erstellen ein Zahlungsabwicklungssystem, in dem verschiedene Zahlungsmethoden austauschbar verwendet werden können! Du erstellst zwei völlig unabhängige Zahlungsklassen, die eine gemeinsame Schnittstelle, eine :process(amount)-Methode, verwenden. Dadurch kann jeder Code, der Zahlungen verarbeitet, mit beiden Typen arbeiten, ohne den Unterschied zu kennen.

Du organisierst deinen Code über drei Dateien:

  • CreditCard.lua: Eine Zahlungsklasse mit einem :new(cardNumber)-Konstruktor, der die Kartennummer speichert. Ihre :process(amount)-Methode soll Charging {amount} to card {cardNumber} ausgeben.
  • BankTransfer.lua: Eine vollständig separate Zahlungsklasse mit einem :new(accountId)-Konstruktor. Ihre :process(amount)-Methode soll Transferring {amount} from account {accountId} ausgeben.
  • main.lua: Erstelle eine Funktion namens handlePayment(paymentMethod, amount), die ein beliebiges Zahlungsobjekt und einen Betrag akzeptiert und anschließend :process(amount) für dieses Objekt aufruft. Diese Funktion muss nicht wissen, ob sie mit einer Kreditkarte oder einer Banküberweisung arbeitet. Sie erwartet lediglich, dass das Objekt eine :process()-Methode besitzt.

Du erhältst drei Eingaben:

  1. Eine Kreditkartennummer (z. B. 4532-1234-5678)
  2. Eine Bankkonto-ID (z. B. ACC-9876)
  3. Einen Zahlungsbetrag (z. B. 150)

Erstelle in deiner Hauptdatei jeweils eine Instanz jedes Zahlungstyps unter Verwendung der ersten beiden Eingaben. Verwende dann deine handlePayment-Funktion, um den angegebenen Betrag über beide Zahlungsmethoden zu verarbeiten: zuerst über die Kreditkarte, dann über die Banküberweisung.

Wenn die Eingaben beispielsweise 1111-2222-3333, ACC-5555 und 75 lauten, sollte die Ausgabe folgendermaßen aussehen:

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

Das Schöne an diesem Entwurf ist, dass deine handlePayment-Funktion mit jedem Objekt funktioniert, das :process() implementiert. Du könntest morgen eine PayPal-Klasse hinzufügen, und solange sie eine :process(amount)-Methode besitzt, würde sie ohne Änderungen mit deiner bestehenden Funktion funktionieren!

Probier es selbst

-- Importiere die Zahlungsklassen
local CreditCard = require('CreditCard')
local BankTransfer = require('BankTransfer')

-- Eingaben lesen
local cardNumber = io.read()
local accountId = io.read()
local amount = tonumber(io.read())

-- TODO: Erstelle die handlePayment-Funktion, die ein beliebiges Zahlungsobjekt akzeptiert
-- und einen Betrag, und dann :process(amount) darauf aufruft


-- TODO: Erstelle Instanzen von CreditCard und BankTransfer mit den Eingaben


-- TODO: Verwende handlePayment, um den Betrag über beide Zahlungsmethoden zu verarbeiten
-- Zuerst die Kreditkarte, dann die Banküberweisung
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Lua-Compiler