Tabela Singleton
Część sekcji Programowanie obiektowe ścieżki Lua w Coddy. Lekcja 63 z 70.
Czasami w całym programie potrzebujesz dokładnie jednej instancji czegoś: menedżera konfiguracji, systemu logowania lub ustawień gry. Tworzenie wielu kopii mogłoby powodować problemy: która z nich ma prawidłowe dane? Wzorzec Singleton gwarantuje, że istnieje tylko jedna instancja.
W Lua implementacja wzorca Singleton jest zaskakująco prosta dzięki temu, jak działają moduły. Gdy używasz require do załadowania modułu, Lua buforuje wynik. Kolejne wywołania require zwracają zbuforowaną tabelę, a nie nową.
-- GameConfig.lua
local GameConfig = {
volume = 80,
difficulty = "normal"
}
function GameConfig:setVolume(v)
self.volume = v
end
return GameConfig
Teraz, w dowolnym miejscu programu:
local config1 = require("GameConfig")
local config2 = require("GameConfig")
config1:setVolume(50)
print(config2.volume) -- 50 (ta sama tabela!)
Zarówno config1, jak i config2 odwołują się do dokładnie tej samej tablicy. Zmiany wprowadzone za pomocą jednej zmiennej są widoczne przez drugą. Dzieje się tak automatycznie, ponieważ Lua zapisuje zwróconą wartość w package.loaded i używa jej ponownie podczas wywołania require.
W przeciwieństwie do wzorca Fabryki, który tworzy nowe obiekty, Singleton gwarantuje współdzielony stan w całej aplikacji. To idealne rozwiązanie w przypadku ustawień globalnych, do których różne części kodu muszą mieć dostęp i które muszą modyfikować w spójny sposób.
Wyzwanie
ŁatwyStwórzmy menedżer ustawień gry z wykorzystaniem wzorca Singleton! Utworzysz moduł konfiguracji, który zapewni, że każda część programu będzie korzystać z dokładnie tych samych ustawień — bez względu na to, ile razy moduł zostanie załadowany.
Rozmieścisz kod w dwóch plikach:
GameSettings.lua: Utwórz moduł Singleton przechowujący konfigurację gry. Tabela ustawień powinna zawieraćmusicVolume(wartość początkowa 70),sfxVolume(wartość początkowa 100) orazdifficulty(wartość początkowa"normal"). Dodaj trzy metody::setMusicVolume(value),:setSfxVolume(value)i:setDifficulty(level), które będą modyfikować te ustawienia. Pamiętaj, aby na końcu modułu zwrócić tabelę!main.lua: Pokaż, że Singleton działa, ładując moduł GameSettings dwukrotnie do różnych zmiennych. Użyj pierwszej zmiennej, aby zmienić głośność muzyki i poziom trudności. Następnie użyj drugiej zmiennej, aby wypisać wszystkie trzy ustawienia. Jeśli Singleton działa poprawnie, obie zmienne odwołują się do tej samej tabeli, więc zmiany wprowadzone za pomocą jednej będą widoczne za pomocą drugiej.
Otrzymasz dwa dane wejściowe:
- Nowa głośność muzyki (liczba)
- Nowy poziom trudności (ciąg znaków, taki jak
"easy"lub"hard")
Po zmodyfikowaniu ustawień za pomocą pierwszej zmiennej wypisz trzy ustawienia, korzystając z drugiej zmiennej, każde w osobnym wierszu i w następującej kolejności: głośność muzyki, głośność efektów dźwiękowych, a następnie poziom trudności.
Na przykład, jeśli dane wejściowe to 50 i hard, wynikiem powinno być:
50
100
hardJeśli dane wejściowe to 25 i easy, wynikiem powinno być:
25
100
easyZwróć uwagę, że głośność efektów dźwiękowych pozostaje domyślną wartością 100, ponieważ jej nie zmienialiśmy, ale głośność muzyki i poziom trudności odzwierciedlają zmiany wprowadzone za pomocą pierwszej zmiennej, co dowodzi, że obie zmienne wskazują tę samą instancję Singletona!
Spróbuj swoich sił
-- main.lua - Demonstronstracja wzorca Singleton
-- Wczytaj dane wejściowe
local newMusicVolume = tonumber(io.read())
local newDifficulty = io.read()
-- TODO: Załaduj moduł GameSettings do zmiennej (np. settings1)
-- TODO: Załaduj moduł GameSettings PONOWNIE do INNEJ zmiennej (np. settings2)
-- (Jeśli Singleton działa poprawnie, obie zmienne odwołują się do tej samej tabeli!)
-- TODO: Użyj PIERWSZEJ zmiennej, aby:
-- - Ustawić głośność muzyki na newMusicVolume
-- - Ustawić poziom trudności na newDifficulty
-- TODO: Użyj DRUGIEJ zmiennej, aby wypisać ustawienia
-- Wypisz każdą wartość w osobnej linii: musicVolume, sfxVolume, difficulty
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Koncepcja „self”
Tabele z funkcjamiJawne „self”Składnia z dwukropkiemKropka czy dwukropekPodsumowanie — poruszający się punkt4Projekt: Bank cyfrowy
Konfiguracja projektuMetoda wpłaty7Polimorfizm i nadpisywanie
Nadpisywanie metodWywoływanie metod klasy nadrzędnejTypowanie kaczeWspólny interfejsSprawdzanie typuPodsumowanie — role pracowników10Projekt: Menedżer kształtów
Konfiguracja projektuKlasa prostokątaKlasa kołaMetoda obwoduKolekcja kształtówPole całkowiteFiltrowanie kształtów2Wzorzec prototypu klasy
Koncepcja prototypuŁączenie za pomocą __indexKonstruktor :new()Inicjalizacja atrybutówNiezależne instancjePodsumowanie – fabryka samochodów5Przeciążanie operatorów w OOP
Dodawanie obiektówOdejmowanie obiektówŁączenie obiektówPorównywanie obiektów (<, >)Powtórka — działania na portfelu8Hermetyzacja
Konwencje nazewnictwaDomknięcia dla prywatnościDostęp za pomocą domknięćTabele tylko do odczytuLogika walidacjiPodsumowanie — bezpieczny sejf11Wzorce projektowe (podstawy)
Funkcje fabrykująceTabela SingletonWzorzec iteratoraObserwator (nasłuchujący)Powtórka — fabryka loggera3Stan i zachowanie obiektu
Zmienne instancjiMetody dostępoweMetody ustawiająceWłaściwości obliczaneFormatowanie ciągów znakówSprawdzanie równościPodsumowanie – ocena ucznia6Podstawy dziedziczenia
Konfiguracja dziedziczeniaDziedziczenie metodRozszerzanie konstruktoraDodawanie metod potomnychWspólne i unikalne elementyPowtórka — hierarchia kształtówPoćwicz samodzielnie: Kompilator Lua online