Closure per la privacy
Fa parte della sezione Programmazione orientata agli oggetti del percorso Lua di Coddy. Lezione 44 di 70.
Le convenzioni di denominazione segnalano l'intento, ma non impediscono davvero l'accesso. Chiunque può comunque scrivere player._health = 999 e funziona. Per una vera privacy, Lua offre un approccio diverso: memorizzare i dati in variabili locali all'interno del costruttore invece che su self.
L'intuizione fondamentale è che le variabili locali all'interno di una funzione sono accessibili solo all'interno di quella funzione e di qualsiasi funzione definita al suo interno. Questo si chiama closure:
local Counter = {}
Counter.__index = Counter
function Counter:new()
local count = 0 -- Davvero privato! Non è su self.
local obj = {}
setmetatable(obj, Counter)
return obj
endIn questo esempio, count esiste solo all'interno della funzione :new(). Non è memorizzato in obj, quindi non c'è modo di accedervi dall'esterno: myCounter.count restituisce nil, e non c'è un campo con prefisso underscore da aggirare.
La variabile count si trova nell'ambito della closure. Rimane completamente nascosta al codice esterno. (Così com'è scritto, nulla cattura ancora count; nella prossima lezione aggiungerai metodi all'interno del costruttore che la includono nella closure, così persisterà per tutto il tempo in cui esiste l'oggetto.) A differenza di self._count, che è solo un suggerimento cortese, questo approccio rende i dati davvero inaccessibili.
Naturalmente, i dati nascosti non sono utili se non puoi mai interagire con essi. Nella prossima lezione, imparerai a definire metodi all'interno del costruttore che possono leggere e modificare queste variabili private, offrendoti un accesso controllato e mantenendo al contempo un vero incapsulamento.
Sfida
FacileCreiamo una classe SecretKeeper che dimostri la vera privacy dei dati usando le closure! A differenza della convenzione del trattino basso della lezione precedente, questo approccio rende i dati davvero inaccessibili dall'esterno dell'oggetto.
Organizzerai il codice in due file:
SecretKeeper.lua: crea una classe in cui il segreto viene memorizzato in una variabile locale all'interno del costruttore, non inself. Il costruttore:new(secretValue)dovrebbe memorizzare il segreto in una variabile locale che esiste solo all'interno della closure. Per ora, l'oggetto non avrà modo di accedere a questo segreto (lo vedrai nella prossima lezione!), ma dovresti anche memorizzare nell'istanza un campo pubbliconamecon valore"Keeper", così possiamo verificare che l'oggetto funzioni.main.lua: richiedi il modulo SecretKeeper e leggi un valore segreto dall'input. Crea un'istanza di SecretKeeper con quel segreto. Poi dimostra che il segreto è davvero privato stampando tre cose:- Il campo
namedel custode (dovrebbe stampareKeeper) - Cosa succede quando provi ad accedere a
keeper.secret(dovrebbe esserenil) - Cosa succede quando provi ad accedere a
keeper._secret(dovrebbe essere anch'essonil)
- Il campo
Riceverai un input:
- Il valore segreto da memorizzare (ad es.
TopSecret123)
L'output dovrebbe essere composto da tre righe che mostrano che, mentre il campo pubblico funziona, il segreto è completamente nascosto:
Name: Keeper
secret: nil
_secret: nilL'output sarà lo stesso indipendentemente dal segreto che inserisci, perché non c'è modo di accedervi! La variabile locale all'interno del costruttore è invisibile al mondo esterno. Questo dimostra che la privacy basata sulle closure è fondamentalmente diversa dalla convenzione di denominazione con il trattino basso.
Per esempio, se l'input è MyPassword, l'output dovrebbe comunque essere:
Name: Keeper
secret: nil
_secret: nilProvalo tu
-- Carica il modulo SecretKeeper
local SecretKeeper = require('SecretKeeper')
-- Leggi il valore segreto dall'input
local secretValue = io.read()
-- TODO: Crea un'istanza di SecretKeeper con il valore segreto
-- TODO: Stampa il campo name di keeper (formato: "Name: <name>")
-- TODO: Stampa cosa succede quando si accede a keeper.secret (formato: "secret: <value>")
-- TODO: Stampa cosa succede quando si accede a keeper._secret (formato: "_secret: <value>")
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Il concetto di «self»
Tabelle con funzioni«self» esplicitoLa sintassi dei due puntiPunto o due puntiRiepilogo - Spostare il punto4Progetto: Banca digitale
Configurazione del progettoMetodo di deposito2Schema del prototipo di classe
Il concetto di prototipoCollegare con __indexIl costruttore :new()Inizializzare gli attributiIstanze indipendentiRiepilogo - Fabbrica di auto5Sovraccarico degli operatori nella OOP
Sommare oggettiSottrarre oggettiConcatenare oggettiConfrontare oggetti (<, >)Ripasso - Calcoli con il portafoglio8Incapsulamento
Convenzioni di denominazioneClosure per la privacyAccesso tramite closureTabelle di sola letturaLogica di convalidaRiepilogo - Cassaforte sicura11Modelli di progettazione (base)
Funzioni factoryTabella singletonPattern IteratorObserver (listener)Riepilogo - Factory per logger3Stato e comportamento degli oggetti
Variabili di istanzaMetodi getterMetodi setterProprietà calcolateFormattazione delle stringheControlli di uguaglianzaRiepilogo - Voto dello studente6Fondamenti dell'ereditarietà
Configurare l'ereditarietàEreditare i metodiEstendere il costruttoreAggiungere metodi alla classe figliaCondiviso vs specificoRiepilogo - Gerarchia delle formeEsercitati da solo: Compilatore Lua online