Domknięcia dla prywatności
Część sekcji Programowanie obiektowe ścieżki Lua w Coddy. Lekcja 44 z 70.
Konwencje nazewnictwa sygnalizują intencję, ale w rzeczywistości nie uniemożliwiają dostępu. Każdy nadal może napisać player._health = 999 i to zadziała. Aby zapewnić prawdziwą prywatność, Lua oferuje inne podejście: przechowywanie danych w zmiennych lokalnych wewnątrz konstruktora zamiast w self.
Kluczowy wniosek jest taki, że zmienne lokalne wewnątrz funkcji są dostępne tylko w tej funkcji oraz w funkcjach zdefiniowanych wewnątrz niej. Nazywa się to domknięciem:
local Counter = {}
Counter.__index = Counter
function Counter:new()
local count = 0 -- Naprawdę prywatna! Nie znajduje się w self.
local obj = {}
setmetatable(obj, Counter)
return obj
endW tym przykładzie count istnieje tylko wewnątrz funkcji :new(). Nie jest przechowywane w obj, więc nie można uzyskać do niego dostępu z zewnątrz: myCounter.count zwraca nil i nie ma pola z prefiksem podkreślenia, które można by obejść.
Zmienna count znajduje się w zakresie domknięcia. Pozostaje całkowicie ukryta przed kodem zewnętrznym. (W obecnej postaci nic jeszcze nie przechwytuje count; w następnej lekcji dodasz metody wewnątrz konstruktora, które będą ją obejmować domknięciem, dzięki czemu będzie istnieć tak długo, jak sam obiekt). W przeciwieństwie do self._count, który jest tylko uprzejmą sugestią, to podejście sprawia, że dane są naprawdę niedostępne.
Oczywiście ukryte dane są bezużyteczne, jeśli nie możesz z nimi wchodzić w interakcję. W następnej lekcji dowiesz się, jak definiować metody wewnątrz konstruktora, które mogą odczytywać i modyfikować te prywatne zmienne, zapewniając kontrolowany dostęp przy zachowaniu pełnej enkapsulacji.
Wyzwanie
ŁatwyZbudujmy klasę SecretKeeper, która pokazuje prawdziwą prywatność danych za pomocą domknięć! W przeciwieństwie do konwencji z podkreśleniem z poprzedniej lekcji, to podejście sprawia, że dane są naprawdę niedostępne spoza obiektu.
Rozmieścisz swój kod w dwóch plikach:
SecretKeeper.lua: Utwórz klasę, w której sekret jest przechowywany w zmiennej lokalnej wewnątrz konstruktora, a nie wself. Konstruktor:new(secretValue)powinien przechowywać sekret w zmiennej lokalnej, która istnieje tylko w obrębie domknięcia. Na razie obiekt nie będzie miał żadnego sposobu dostępu do tego sekretu (to pojawi się w następnej lekcji!), ale zapisz też publiczne polenameo wartości"Keeper"w instancji, abyśmy mogli sprawdzić, czy obiekt działa.main.lua: Załaduj moduł SecretKeeper i odczytaj sekret z wejścia. Utwórz instancję SecretKeeper z tym sekretem. Następnie pokaż, że sekret jest naprawdę prywatny, wypisując trzy rzeczy:- Pole
nameobiektu keeper (powinno wypisaćKeeper) - Co się stanie, gdy spróbujesz uzyskać dostęp do
keeper.secret(powinno byćnil) - Co się stanie, gdy spróbujesz uzyskać dostęp do
keeper._secret(również powinno byćnil)
- Pole
Otrzymasz jedno wejście:
- Wartość sekretu do przechowania (np.
TopSecret123)
Wynikiem powinny być trzy wiersze pokazujące, że publiczne pole działa, a sekret jest całkowicie ukryty:
Name: Keeper
secret: nil
_secret: nilWynik będzie taki sam niezależnie od tego, jaki sekret podasz, ponieważ nie ma sposobu, aby uzyskać do niego dostęp! Zmienna lokalna wewnątrz konstruktora jest niewidoczna dla świata zewnętrznego. To dowodzi, że prywatność oparta na domknięciach zasadniczo różni się od konwencji nazewnictwa z podkreśleniem.
Na przykład, jeśli wejściem jest MyPassword, wynikiem nadal powinno być:
Name: Keeper
secret: nil
_secret: nilSpróbuj swoich sił
-- Załaduj moduł SecretKeeper
local SecretKeeper = require('SecretKeeper')
-- Odczytaj tajną wartość z wejścia
local secretValue = io.read()
-- TODO: Utwórz instancję SecretKeeper z tajną wartością
-- TODO: Wypisz pole name obiektu keeper (format: "Name: <name>")
-- TODO: Wypisz, co się stanie przy dostępie do keeper.secret (format: "secret: <value>")
-- TODO: Wypisz, co się stanie przy dostępie do keeper._secret (format: "_secret: <value>")
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