Menu
Coddy logo textTech

Closures pour la confidentialité

Fait partie de la section Programmation orientée objet du Journey Lua de Coddy. Leçon 44 sur 70.

Les conventions de nommage signalent l’intention, mais elles n’empêchent pas réellement l’accès. N’importe qui peut toujours écrire player._health = 999 et cela fonctionne. Pour une véritable confidentialité, Lua propose une approche différente : stocker les données dans des variables locales à l’intérieur du constructeur plutôt que dans self.

L’idée clé est que les variables local à l’intérieur d’une function ne sont accessibles qu’au sein de cette function et de toutes les fonctions qui y sont définies. C’est ce qu’on appelle une closure :

local Counter = {}
Counter.__index = Counter

function Counter:new()
    local count = 0  -- Vraiment privé ! Pas sur self.
    
    local obj = {}
    setmetatable(obj, Counter)
    return obj
end

Dans cet exemple, count n’existe qu’à l’intérieur de la :new() fonction. Il n’est pas stocké dans obj, il est donc impossible d’y accéder depuis l’extérieur : myCounter.count renvoie nil, et il n’existe aucun champ préfixé par un underscore permettant de contourner cela.

La variable count vit dans la portée de la closure. Elle reste complètement masquée du code externe. (Tel que c’est écrit, rien ne capture encore count ; dans la prochaine leçon, tu ajouteras des méthodes à l’intérieur du constructeur qui forment une closure autour de cette variable, afin qu’elle persiste aussi longtemps que l’objet.) Contrairement à self._count, qui n’est qu’une suggestion polie, cette approche rend les données véritablement inaccessibles.

Bien sûr, les données cachées ne sont pas utiles si vous ne pouvez jamais interagir avec elles. Dans la prochaine leçon, vous apprendrez à définir des méthodes à l’intérieur du constructeur qui peuvent lire et modifier ces variables privées, vous donnant ainsi un accès contrôlé tout en maintenant une véritable encapsulation.

challenge icon

Défi

Facile

Construisons une classe SecretKeeper qui démontre une véritable confidentialité des données à l’aide des fermetures ! Contrairement à la convention du trait de soulignement de la leçon précédente, cette approche rend les données véritablement inaccessibles depuis l’extérieur de l’objet.

Vous allez organiser votre code sur deux fichiers :

  • SecretKeeper.lua : créez une classe dans laquelle le secret est stocké dans une variable locale à l’intérieur du constructeur, et non dans self. Le constructeur :new(secretValue) doit stocker le secret dans une variable locale qui n’existe qu’à l’intérieur de la fermeture. Pour l’instant, l’objet n’aura aucun moyen d’accéder à ce secret (cela viendra dans la prochaine leçon !), mais vous devez également stocker un champ public name défini sur "Keeper" dans l’instance afin que nous puissions vérifier que l’objet fonctionne.
  • main.lua : importez votre module SecretKeeper et lisez une valeur secrète depuis l’entrée. Créez une instance de SecretKeeper avec ce secret. Montrez ensuite que le secret est véritablement privé en affichant trois éléments :
    • Le champ name de l’instance keeper (devrait afficher Keeper)
    • Ce qui se passe lorsque vous essayez d’accéder à keeper.secret (devrait être nil)
    • Ce qui se passe lorsque vous essayez d’accéder à keeper._secret (devrait également être nil)

Vous recevrez une entrée :

  1. La valeur secrète à stocker (par exemple, TopSecret123)

Votre sortie doit comporter trois lignes montrant que, tandis que le champ public fonctionne, le secret est complètement masqué :

Name: Keeper
secret: nil
_secret: nil

La sortie sera la même quel que soit le secret que vous fournissez, car il n’existe aucun moyen d’y accéder ! La variable locale à l’intérieur du constructeur est invisible depuis l’extérieur. Cela prouve que la confidentialité fondée sur les fermetures est fondamentalement différente de la convention de nommage avec un trait de soulignement.

Par exemple, si l’entrée est MyPassword, la sortie devra tout de même être :

Name: Keeper
secret: nil
_secret: nil

Essayez vous-même

-- Requiert le module SecretKeeper
local SecretKeeper = require('SecretKeeper')

-- Lit la valeur secrète depuis l'entrée
local secretValue = io.read()

-- TODO: Créer une instance de SecretKeeper avec la valeur secrète

-- TODO: Afficher le champ name du keeper (format : "Name: <name>")

-- TODO: Afficher ce qui se passe lors de l'accès à keeper.secret (format : "secret: <value>")

-- TODO: Afficher ce qui se passe lors de l'accès à keeper._secret (format : "_secret: <value>")
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Compilateur Lua en ligne