Singleton-Tabelle
Teil des Abschnitts Object Oriented Programming der Lua-Journey von Coddy — Lektion 63 von 70.
Manchmal benötigen Sie genau eine Instanz von etwas in Ihrem gesamten Programm – einen Konfigurationsmanager, ein Logging-System oder Spieleinstellungen. Das Erstellen mehrerer Kopien würde Probleme verursachen: Welche davon enthält die korrekten Daten? Das Singleton-Pattern stellt sicher, dass nur eine einzige Instanz existiert.
In Lua ist die Implementierung eines Singletons dank der Funktionsweise von Modulen überraschend einfach. Wenn Sie ein Modul mit require einbinden, speichert Lua das Ergebnis im Cache. Nachfolgende Aufrufe von require geben die im Cache gespeicherte Tabelle zurück, keine neue.
-- GameConfig.lua
local GameConfig = {
volume = 80,
difficulty = "normal"
}
function GameConfig:setVolume(v)
self.volume = v
end
return GameConfig
Jetzt, an jeder beliebigen Stelle in Ihrem Programm:
local config1 = require("GameConfig")
local config2 = require("GameConfig")
config1:setVolume(50)
print(config2.volume) -- 50 (dieselbe Tabelle!)
Sowohl config1 als auch config2 referenzieren genau dieselbe Tabelle. Änderungen, die über eine Variable vorgenommen werden, sind über die andere sichtbar. Dies geschieht automatisch, da Luas require den zurückgegebenen Wert in package.loaded speichert und wiederverwendet.
Im Gegensatz zum Factory-Pattern, das neue Objekte erstellt, garantiert ein Singleton einen gemeinsamen Zustand über Ihre gesamte Anwendung hinweg. Dies ist ideal für globale Einstellungen, auf die mehrere Teile Ihres Codes konsistent zugreifen und die sie konsistent ändern müssen.
Aufgabe
EinfachLassen Sie uns einen Spieleinstellungen-Manager mit dem Singleton-Muster erstellen! Sie werden ein Konfigurationsmodul erstellen, das sicherstellt, dass jeder Teil Ihres Programms auf genau dieselben Einstellungen zugreift – egal wie oft es angefordert wird.
Sie werden Ihren Code auf zwei Dateien verteilen:
GameSettings.lua: Erstellen Sie ein Singleton-Modul, das die Spielkonfiguration speichert. Ihre Einstellungstabelle solltemusicVolume(beginnend bei 70),sfxVolume(beginnend bei 100) unddifficulty(beginnend bei"normal") enthalten. Fügen Sie drei Methoden hinzu::setMusicVolume(value),:setSfxVolume(value)und:setDifficulty(level), um diese Einstellungen zu ändern. Denken Sie daran, die Tabelle am Ende des Moduls zurückzugeben!main.lua: Demonstrieren Sie, dass das Singleton funktioniert, indem Sie Ihr GameSettings-Modul zweimal in verschiedene Variablen laden (require). Verwenden Sie die erste Variable, um die Musiklautstärke und den Schwierigkeitsgrad zu ändern. Verwenden Sie dann die zweite Variable, um alle drei Einstellungen auszugeben – wenn das Singleton korrekt funktioniert, referenzieren beide Variablen dieselbe Tabelle, sodass Änderungen, die über die eine vorgenommen wurden, über die andere sichtbar sind.
Sie erhalten zwei Eingaben:
- Neue Musiklautstärke (eine Zahl)
- Neuer Schwierigkeitsgrad (ein String wie
"easy"oder"hard")
Nachdem Sie die Einstellungen über die erste Variable geändert haben, geben Sie die drei Einstellungen über die zweite Variable aus, jede in einer eigenen Zeile in dieser Reihenfolge: Musiklautstärke, SFX-Lautstärke und dann Schwierigkeitsgrad.
Wenn die Eingaben beispielsweise 50 und hard sind, sollte die Ausgabe wie folgt aussehen:
50
100
hardWenn die Eingaben 25 und easy sind, sollte die Ausgabe wie folgt aussehen:
25
100
easyBeachten Sie, dass die SFX-Lautstärke auf ihrem Standardwert von 100 bleibt, da wir sie nie geändert haben – aber die Musiklautstärke und der Schwierigkeitsgrad spiegeln die über die erste Variable vorgenommenen Änderungen wider, was beweist, dass beide Variablen auf dieselbe Singleton-Instanz zeigen!
Spickzettel
Das Singleton-Muster stellt sicher, dass während des gesamten Programms nur eine einzige Instanz eines Objekts existiert. Dies ist nützlich für Konfigurationsmanager, Protokollierungssysteme oder Spieleinstellungen, bei denen mehrere Instanzen zu Dateninkonsistenzen führen würden.
In Lua wird das Singleton-Muster auf natürliche Weise durch das Modulsystem implementiert. Wenn Sie ein Modul mit require laden, speichert Lua das Ergebnis in package.loaded zwischen. Nachfolgende Aufrufe von require geben dieselbe zwischengespeicherte Tabelle zurück, keine neue.
Erstellen eines Singleton-Moduls:
-- GameConfig.lua
local GameConfig = {
volume = 80,
difficulty = "normal"
}
function GameConfig:setVolume(v)
self.volume = v
end
return GameConfig
Verwendung des Singletons:
local config1 = require("GameConfig")
local config2 = require("GameConfig")
config1:setVolume(50)
print(config2.volume) -- 50 (dieselbe Tabelle!)
Sowohl config1 und config2 beziehen sich auf genau dieselbe Tabelle. Änderungen, die über eine Variable vorgenommen werden, sind sofort über die andere sichtbar, was einen gemeinsamen Zustand über die gesamte Anwendung hinweg gewährleistet.
Das Singleton-Muster unterscheidet sich vom Factory-Muster: Während Factory neue Objekte erstellt, garantiert Singleton eine einzige gemeinsame Instanz mit konsistentem Zustand.
Probier es selbst
-- main.lua - Demonstriert das Singleton-Muster
-- Eingaben lesen
local newMusicVolume = tonumber(io.read())
local newDifficulty = io.read()
-- TODO: GameSettings-Modul in eine Variable einbinden (z. B. settings1)
-- TODO: GameSettings-Modul ERNEUT in eine ANDERE Variable einbinden (z. B. settings2)
-- (Wenn das Singleton korrekt funktioniert, referenzieren beide Variablen dieselbe Tabelle!)
-- TODO: Verwende die ERSTE Variable, um:
-- - Die Musiklautstärke auf newMusicVolume zu setzen
-- - Den Schwierigkeitsgrad auf newDifficulty zu setzen
-- TODO: Verwende die ZWEITE Variable, um die Einstellungen auszugeben
-- Gib jede in einer eigenen Zeile aus: musicVolume, sfxVolume, difficulty
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
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-Klasse2Klassen-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 Tresor11Entwurfsmuster (Lite)
Factory-FunktionenSingleton-TabelleIterator-MusterObserver (Listener)Wiederholung – Logger-Factory3Objektzustand 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