Closures für private Daten
Teil des Abschnitts Objektorientierte Programmierung 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 zugänglich sind – und in allen Funktionen, die darin definiert 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
endIn 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 lebt im Scope des Closures. Sie bleibt vor externem Code vollständig verborgen. (Wie geschrieben, erfasst noch nichts count; in der nächsten Lektion werden Sie Methoden innerhalb des Konstruktors hinzufügen, die es umschließen, sodass es so lange bestehen bleibt, wie das Objekt existiert.) Im Gegensatz zu self._count, was nur eine höfliche Empfehlung ist, macht dieser Ansatz die Daten tatsächlich unzugänglich.
Natürlich sind versteckte Daten nicht nützlich, wenn man nie mit ihnen interagieren kann. In der nächsten Lektion lernst du, wie du Methoden innerhalb des Konstruktors definierst, die diese privaten Variablen lesen und ändern können – was dir kontrollierten Zugriff ermöglicht, während die echte Kapselung erhalten bleibt.
Aufgabe
EinfachLassen Sie uns eine SecretKeeper-Klasse bauen, die echte Datenkapselung 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 über zwei Dateien organisieren:
SecretKeeper.lua: Erstellen Sie eine Klasse, in der das Geheimnis in einer lokalen Variable innerhalb des Konstruktors gespeichert wird – nicht aufself. 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 öffentlichesname-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 (sollteKeeperausgeben) - Was passiert, wenn Sie versuchen, auf
keeper.secretzuzugreifen (solltenilsein) - Was passiert, wenn Sie versuchen, auf
keeper._secretzuzugreifen (sollte ebenfallsnilsein)
- Das
Sie erhalten eine Eingabe:
- 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: nilDie 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: nilProbier es selbst
-- Binde das SecretKeeper-Modul ein
local SecretKeeper = require('SecretKeeper')
-- Lies den geheimen Wert aus 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>")
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Das 'self'-Konzept
Tabellen mit FunktionenExplizites 'self'Die Doppelpunkt-SyntaxPunkt vs. DoppelpunktZusammenfassung – Beweglicher Punkt4Projekt: Digitale Bank
Projekt-SetupEinzahlungsmethode7Polymorphie & Überschreiben
Methoden überschreibenElternmethoden aufrufenDuck TypingGemeinsames InterfaceTyp prüfenRückblick - Mitarbeiterrollen10Projekt: Formen-Manager
Projekt-SetupRechteck-KlasseKreis-KlasseUmfang-MethodeFormen-SammlungGesamtflächeFormen filtern2Klassen-Prototyp-Muster
Das Prototyp-KonzeptVerknüpfung mit __indexDer :new()-KonstruktorAttribute initialisierenUnabhängige InstanzenZusammenfassung – Autofabrik5Operatorüberladung in OOP
Objekte addierenObjekte subtrahierenObjekte verkettenObjekte vergleichen (<, >)Rückblick – Wallet-Mathematik8Kapselung
NamenskonventionenClosures für private DatenZugriff über ClosuresSchreibgeschützte TabellenValidierungslogikZusammenfassung – Sicherer Tresor3Objektzustand und -verhalten
InstanzvariablenGetter-MethodenSetter-MethodenBerechnete EigenschaftenStrings formatierenGleichheitsprüfungenZusammenfassung – Studentennote6Grundlagen der Vererbung
Das Vererbungs-SetupMethoden vererbenDen Konstruktor erweiternChild-Methoden hinzufügenGemeinsam vs. EinzigartigZusammenfassung - Formen-Hierarchie9Komposition & Mixins
Has-A-BeziehungDelegationEinfache MixinsAnwendung mehrerer MixinsMixins vs. VererbungZusammenfassung – Roboter-Montage12Abschluss-Herausforderungen
Wiederholung - InventarsystemWiederholung - BenutzerverwaltungWiederholung - Score-TrackerFinaler OOP-Check