Menu
Coddy logo textTech

Closures für private Daten

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

Namenskonventionen signalisieren Absicht, aber sie verhindern den Zugriff nicht wirklich. Jeder kann immer noch player._health = 999 schreiben und es funktioniert. Für echte Privatsphäre bietet Lua einen anderen Ansatz: das Speichern von Daten in lokalen Variablen innerhalb des Konstruktors anstatt auf self.

Die entscheidende Erkenntnis ist, dass lokale Variablen innerhalb einer Funktion nur innerhalb dieser Funktion – und allen darin definierten Funktionen – zugänglich sind. Dies wird als Closure bezeichnet:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- Wirklich privat! Nicht auf self.
    
    local obj = {}
    setmetatable(obj, Counter)
    return obj
end

In diesem Beispiel existiert count nur innerhalb der :new()-Funktion. Es wird nicht in obj gespeichert, daher gibt es keine Möglichkeit, von außen darauf zuzugreifen – myCounter.count gibt nil zurück, und es gibt kein Feld mit Unterstrich-Präfix, das man umgehen könnte.

Die Variable count befindet sich im Scope des Closures. Sie bleibt bestehen, solange das Objekt existiert, ist aber vor externem Code vollständig verborgen. Im Gegensatz zu self._count, was lediglich eine höfliche Empfehlung ist, macht dieser Ansatz die Daten tatsächlich unzugänglich.

Natürlich sind verborgene Daten nicht nützlich, wenn man niemals mit ihnen interagieren kann. In der nächsten Lektion wirst du lernen, wie man Methoden innerhalb des Konstruktors definiert, die diese privaten Variablen lesen und ändern können – was dir kontrollierten Zugriff ermöglicht, während die echte Kapselung beibehalten wird.

challenge icon

Aufgabe

Einfach

Lassen Sie uns eine SecretKeeper-Klasse erstellen, die echte Datensicherheit mithilfe von Closures demonstriert! Im Gegensatz zur Unterstrich-Konvention aus der vorherigen Lektion macht dieser Ansatz Daten von außerhalb des Objekts tatsächlich unzugänglich.

Sie werden Ihren Code auf zwei Dateien aufteilen:

  • SecretKeeper.lua: Erstellen Sie eine Klasse, in der das Geheimnis in einer lokalen Variable innerhalb des Konstruktors gespeichert wird – nicht auf self. Der Konstruktor :new(secretValue) sollte das Geheimnis in einer lokalen Variable speichern, die nur innerhalb der Closure existiert. Vorerst wird das Objekt keine Möglichkeit haben, auf dieses Geheimnis zuzugreifen (das kommt in der nächsten Lektion!), aber Sie sollten auch ein öffentliches name-Feld auf der Instanz speichern, das auf "Keeper" gesetzt ist, damit wir überprüfen können, ob das Objekt funktioniert.
  • main.lua: Binden Sie Ihr SecretKeeper-Modul ein und lesen Sie einen Geheimwert aus der Eingabe. Erstellen Sie eine SecretKeeper-Instanz mit diesem Geheimnis. Demonstrieren Sie dann, dass das Geheimnis wirklich privat ist, indem Sie drei Dinge ausgeben:
    • Das name-Feld des Keepers (sollte Keeper ausgeben)
    • Was passiert, wenn Sie versuchen, auf keeper.secret zuzugreifen (sollte nil sein)
    • Was passiert, wenn Sie versuchen, auf keeper._secret zuzugreifen (sollte ebenfalls nil sein)

Sie erhalten eine Eingabe:

  1. Der zu speichernde Geheimwert (z. B. TopSecret123)

Ihre Ausgabe sollte aus drei Zeilen bestehen, die zeigen, dass das öffentliche Feld funktioniert, während das Geheimnis vollständig verborgen ist:

Name: Keeper
secret: nil
_secret: nil

Die Ausgabe wird dieselbe sein, unabhängig davon, welches Geheimnis Sie übergeben – weil es keine Möglichkeit gibt, darauf zuzugreifen! Die lokale Variable innerhalb des Konstruktors ist für die Außenwelt unsichtbar. Dies beweist, dass Closure-basierte Privatsphäre sich grundlegend von der Unterstrich-Namenskonvention unterscheidet.

Wenn die Eingabe beispielsweise MyPassword ist, sollte die Ausgabe immer noch wie folgt lauten:

Name: Keeper
secret: nil
_secret: nil

Spickzettel

Um in Lua wirklich private Daten zu erstellen, speichern Sie diese in lokalen Variablen innerhalb des Konstruktors anstatt in self. Dies nutzt eine Closure – lokale Variablen sind nur innerhalb der Funktion zugänglich, in der sie definiert sind, sowie in allen darin definierten Funktionen.

Beispiel für eine Klasse mit privaten Daten:

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- Wirklich privat! Nicht in self.
    
    local obj = {}
    setmetatable(obj, Counter)
    return obj
end

In diesem Beispiel existiert count nur innerhalb der Funktion :new(). Es wird nicht in obj gespeichert, daher gibt myCounter.count den Wert nil zurück. Es gibt keine Möglichkeit, von außen darauf zuzugreifen – im Gegensatz zu self._count (was lediglich eine Namenskonvention ist), macht dieser Ansatz die Daten tatsächlich unzugänglich.

Die Variable bleibt bestehen, solange das Objekt existiert, bleibt aber vor externem Code vollständig verborgen, was eine echte Kapselung ermöglicht.

Probier es selbst

-- Erfordert das SecretKeeper-Modul
local SecretKeeper = require('SecretKeeper')

-- Liest den geheimen Wert aus der Eingabe
local secretValue = io.read()

-- TODO: Erstelle eine SecretKeeper-Instanz mit dem geheimen Wert

-- TODO: Gib das Namensfeld des Keepers aus (Format: "Name: <name>")

-- TODO: Gib aus, was passiert, wenn auf keeper.secret zugegriffen wird (Format: "secret: <value>")

-- TODO: Gib aus, was passiert, wenn auf keeper._secret zugegriffen wird (Format: "_secret: <value>")
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