Menu
Coddy logo textTech

Closures für private Daten

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

Namenskonventionen signalisieren die Absicht, verhindern den Zugriff jedoch nicht tatsächlich. Jeder kann weiterhin player._health = 999 schreiben, und es funktioniert. Für wahre Privatsphäre bietet Lua einen anderen Ansatz: Daten in lokalen Variablen innerhalb des Konstruktors zu speichern, statt in self.

Die zentrale Erkenntnis ist, dass lokale Variablen innerhalb einer Funktion nur innerhalb dieser Funktion und aller 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 Funktion :new(). Es wird nicht in obj gespeichert, daher gibt es keine Möglichkeit, von außerhalb darauf zuzugreifen: myCounter.count gibt nil zurück, und es gibt kein Feld mit vorangestelltem Unterstrich, das als Umgehung dienen könnte.

Die Variable count lebt im Gültigkeitsbereich der closure. Sie bleibt vor externem Code vollständig verborgen. (So wie es geschrieben ist, erfasst noch nichts count; in der nächsten Lektion wirst du innerhalb des Konstruktors Methoden hinzufügen, die count einschließen, sodass es so lange bestehen bleibt wie das Objekt.) Anders als self._count, das nur ein höflicher Vorschlag ist, macht dieser Ansatz die Daten tatsächlich unzugänglich.

Natürlich sind verborgene Daten nicht nützlich, wenn du nie mit ihnen interagieren kannst. In der nächsten Lektion lernst du, wie du Methoden innerhalb des Konstruktors definierst, die diese privaten Variablen lesen und ändern können. So erhältst du kontrollierten Zugriff und bewahrst gleichzeitig eine echte Kapselung.

challenge icon

Aufgabe

Einfach

Erstellen wir eine SecretKeeper-Klasse, die mithilfe von Closures echten Datenschutz demonstriert! Anders als bei der Unterstrich-Konvention aus der vorherigen Lektion macht dieser Ansatz die Daten von außerhalb des Objekts tatsächlich unzugänglich.

Du wirst deinen Code auf zwei Dateien aufteilen:

  • SecretKeeper.lua: Erstelle 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. Das Objekt wird vorerst keine Möglichkeit haben, auf dieses Geheimnis zuzugreifen (das kommt in der nächsten Lektion!), aber du solltest außerdem ein öffentliches name-Feld mit dem Wert "Keeper" auf der Instanz speichern, damit wir überprüfen können, dass das Objekt funktioniert.
  • main.lua: Binde dein SecretKeeper-Modul ein und lies einen Geheimniswert aus der Eingabe. Erstelle mit diesem Geheimnis eine SecretKeeper-Instanz. Zeige dann, dass das Geheimnis tatsächlich privat ist, indem du drei Dinge ausgibst:
    • Das name-Feld des Keepers (sollte Keeper ausgeben)
    • Was passiert, wenn du versuchst, auf keeper.secret zuzugreifen (sollte nil sein)
    • Was passiert, wenn du versuchst, auf keeper._secret zuzugreifen (sollte ebenfalls nil sein)

Du erhältst eine Eingabe:

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

Deine 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 ist unabhängig davon, welches Geheimnis du übergibst, immer gleich, weil es keine Möglichkeit gibt, darauf zuzugreifen! Die lokale Variable innerhalb des Konstruktors ist für die Außenwelt unsichtbar. Dies beweist, dass der auf Closures basierende Datenschutz sich grundlegend von der Konvention zur Benennung mit Unterstrich unterscheidet.

Wenn die Eingabe beispielsweise MyPassword lautet, sollte die Ausgabe weiterhin so aussehen:

Name: Keeper
secret: nil
_secret: nil

Probier es selbst

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

-- Lies den geheimen Wert von der Eingabe
local secretValue = io.read()

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

-- TODO: Gib das name-Feld 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 Objektorientierte Programmierung

Übe selbstständig: Online-Lua-Compiler