Menu
Coddy logo textTech

Schreibgeschützte Tabellen

Teil des Abschnitts Object Oriented Programming der Lua-Journey von Coddy — Lektion 46 von 70.

Closures bieten echte Privatsphäre, erfordern jedoch die Definition von Methoden innerhalb des Konstruktors. Es gibt einen anderen Ansatz für einen anderen Anwendungsfall: ein Objekt nach der Erstellung schreibgeschützt zu machen. Dies ist nützlich, wenn Felder sichtbar, aber unveränderlich sein sollen – wie die Koordinaten eines Fixpunktes.

Die __newindex-Metamethode fängt Versuche ab, einer Tabelle neue Werte zuzuweisen. Indem man sie alle Zuweisungen blockieren lässt, kann man 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 eigentlichen Daten befinden sich in data, während der Benutzer mit dem leeren proxy interagiert.

Beim Lesen ruft __index Daten von 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 wertvoll für Konfigurationsobjekte, mathematische Konstanten oder alle Daten, die sich nach der Initialisierung niemals ändern sollten. Das Objekt bleibt vollständig lesbar – man kann es nur nicht ändern.

challenge icon

Aufgabe

Einfach

Lass uns eine ImmutableConfig-Klasse bauen, die schreibgeschützte Konfigurationsobjekte erstellt! Einmal erstellt, sollten diese Konfigurationsobjekte das Lesen ihrer Einstellungen ermöglichen, aber alle 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) sollte diese drei Werte intern speichern und dann eine Proxy-Tabelle zurückgeben, die das Lesen aller Felder erlaubt, aber jegliche Änderungen verhindert. Wenn jemand versucht, einen Wert zu ändern, soll Error: Config is read-only ausgegeben werden, anstatt einen tatsächlichen Fehler auszulösen.
  • main.lua: Erwecke deine ImmutableConfig zum Leben! Lies drei Eingaben für die Konfigurationswerte ein, erstelle ein Konfigurationsobjekt und demonstriere dann, dass es korrekt funktioniert, indem du:
    • Alle drei ursprünglichen Werte ausgibst
    • Versuchst, das Feld maxUsers zu ändern (was die Fehlermeldung auslösen sollte)
    • maxUsers erneut ausgibst, um zu beweisen, dass es nicht wirklich geändert wurde

Du wirst drei Eingaben erhalten:

  1. Der Anwendungsname (ein String)
  2. Die Version (ein String)
  3. Die maximale Anzahl an Benutzern (eine Zahl)

Deine Ausgabe sollte die Konfigurationswerte, den Fehler beim Änderungsversuch und die Bestätigung, dass der Wert unverändert geblieben ist, zeigen:

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

Wenn die Eingaben zum Beispiel MyApp, 2.0 und 100 sind, sollte die Ausgabe wie folgt 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 Daten aus der Datentabelle ab, und __newindex fängt alle Schreibversuche ab (und blockiert sie)!

Spickzettel

Die Metamethode __newindex fängt Versuche ab, einer Tabelle neue Werte zuzuweisen. Indem Sie alle Zuweisungen blockieren, können Sie unveränderliche (schreibgeschützte) Objekte erstellen:

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

Das Proxy-Muster verwendet eine leere Tabelle (proxy), mit der der Benutzer interagiert, während die tatsächlichen Daten in einer separaten internen Tabelle (data) liegen:

  • __index ruft beim Lesen Werte aus der data-Tabelle ab
  • __newindex fängt Schreibversuche ab und blockiert diese
local p = ImmutablePoint.new(10, 20)
print(p.x)  -- 10 (Lesen funktioniert)
p.x = 50    -- Error: Cannot modify immutable object

Dieses Muster ist nützlich für Konfigurationsobjekte, mathematische Konstanten oder alle Daten, die nach der Initialisierung unverändert bleiben sollen.

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 Konfigurationsobjekt mit ImmutableConfig.new()

-- TODO: Gib alle drei ursprünglichen Werte in folgendem Format aus:
-- App: {appName}
-- Version: {version}
-- Max Users: {maxUsers}

-- TODO: Versuche, das Feld maxUsers 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 Object Oriented Programming