Menu
Coddy logo textTech

Schreibgeschützte Tabellen

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

Closures bieten echte Privatsphäre, erfordern jedoch, dass Methoden innerhalb des Konstruktors definiert werden. Für einen anderen Anwendungsfall gibt es einen weiteren Ansatz: ein Objekt nach der Erstellung schreibgeschützt zu machen. Das ist nützlich, wenn Felder sichtbar, aber nicht änderbar sein sollen, etwa die Koordinaten eines festen Punkts.

Die Metamethode __newindex fängt Versuche ab, einer Tabelle neue Werte zuzuweisen. Indem du sie alle Zuweisungen blockieren lässt, kannst du ein Objekt einfrieren:

local ImmutablePoint = {}

function ImmutablePoint.new(x, y)
    local data = {x = x, y = y}
    
    local proxy = {}
    setmetatable(proxy, {
        __index = data,
        __newindex = function()
            error("Cannot modify immutable object")
        end
    })
    
    return proxy
end

Der Trick besteht darin, eine Proxy-Tabelle zu verwenden. Die tatsächlichen Daten befinden sich in data, während der Benutzer mit dem leeren proxy interagiert.

Beim Lesen ruft __index Daten aus data ab. Beim Schreiben wird stattdessen __newindex ausgelöst, und wir blockieren dies:

local p = ImmutablePoint.new(10, 20)
print(p.x)  -- 10 (Lesen funktioniert)
p.x = 50    -- Fehler: Unveränderliches Objekt kann nicht geändert werden

Dieses Muster ist für Konfigurationsobjekte, mathematische Konstanten oder beliebige Daten wertvoll, die sich nach der Initialisierung niemals ändern sollten. Das Objekt bleibt vollständig lesbar. Du kannst es nur nicht verändern.

challenge icon

Aufgabe

Einfach

Erstellen wir eine ImmutableConfig-Klasse, die schreibgeschützte Konfigurationsobjekte erzeugt! Nach ihrer Erstellung sollen diese Konfigurationsobjekte das Lesen ihrer Einstellungen ermöglichen, aber sämtliche Versuche, sie zu ändern, vollständig blockieren.

Du wirst deinen Code auf zwei Dateien aufteilen:

  • ImmutableConfig.lua: Erstelle ein Modul, das eingefrorene Konfigurationsobjekte erzeugt. Deine Funktion .new(appName, version, maxUsers) soll diese drei Werte intern speichern und anschließend eine Proxy-Tabelle zurückgeben, die das Lesen aller Felder ermöglicht, aber Änderungen verhindert. Wenn jemand versucht, einen Wert zu ändern, soll sie Error: Config is read-only ausgeben, anstatt tatsächlich einen Fehler auszulösen.
  • main.lua: Erwecke deine ImmutableConfig zum Leben! Lies drei Eingaben für die Konfigurationswerte ein, erstelle ein Konfigurationsobjekt und zeige anschließend durch folgende Schritte, dass es korrekt funktioniert:
    • Alle drei ursprünglichen Werte ausgeben
    • Versuchen, das Feld maxUsers zu ändern (wodurch die Fehlermeldung ausgelöst werden sollte)
    • maxUsers erneut ausgeben, um zu beweisen, dass der Wert tatsächlich nicht geändert wurde

Du erhältst drei Eingaben:

  1. Der Name der Anwendung (eine Zeichenkette)
  2. Die Version (eine Zeichenkette)
  3. Die maximale Benutzerzahl (eine Zahl)

Deine Ausgabe sollte die Konfigurationswerte, den Fehler beim Versuch der Änderung sowie die Bestätigung anzeigen, dass der Wert unverändert geblieben ist:

App: {appName}
Version: {version}
Max Users: {maxUsers}
Error: Config is read-only
Max Users: {maxUsers}

Wenn die Eingaben beispielsweise MyApp, 2.0 und 100 lauten, sollte die Ausgabe folgendermaßen aussehen:

App: MyApp
Version: 2.0
Max Users: 100
Error: Config is read-only
Max Users: 100

Denke daran: Das Proxy-Muster verwendet eine leere Tabelle, mit der der Benutzer interagiert, während die tatsächlichen Daten in einer separaten internen Tabelle liegen. Die Metamethode __index ruft die Daten aus der Datentabelle ab, und __newindex fängt alle Schreibversuche ab (und blockiert sie)!

Probier es selbst

-- Importiere das ImmutableConfig-Modul
local ImmutableConfig = require('ImmutableConfig')

-- Eingaben lesen
local appName = io.read()
local version = io.read()
local maxUsers = tonumber(io.read())

-- TODO: Erstelle ein Config-Objekt mit ImmutableConfig.new()

-- TODO: Gib alle drei Originalwerte im folgenden Format aus:
-- App: {appName}
-- Version: {version}
-- Max Users: {maxUsers}

-- TODO: Versuche, das maxUsers-Feld zu ändern (dies sollte eine Fehlermeldung auslösen)

-- TODO: Gib maxUsers erneut aus, um zu beweisen, dass es nicht geändert wurde
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