Menu
Coddy logo textTech

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
end

In 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.

challenge icon

Sfida

Facile

Creiamo 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 in self. 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 pubblico name con 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 name del custode (dovrebbe stampare Keeper)
    • Cosa succede quando provi ad accedere a keeper.secret (dovrebbe essere nil)
    • Cosa succede quando provi ad accedere a keeper._secret (dovrebbe essere anch'esso nil)

Riceverai un input:

  1. 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: nil

L'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: nil

Provalo 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>")
quiz iconMettiti alla prova

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

Esercitati da solo: Compilatore Lua online