Tables en lecture seule
Fait partie de la section Object Oriented Programming du Journey Lua de Coddy — leçon 46 sur 70.
Les fermetures (closures) offrent une véritable confidentialité, mais elles nécessitent de définir les méthodes à l'intérieur du constructeur. Il existe une autre approche pour un cas d'utilisation différent : rendre un objet en lecture seule après sa création. C'est utile lorsque vous voulez que les champs soient visibles mais non modifiables, comme les coordonnées d'un point fixe.
La métaméthode __newindex intercepte les tentatives d'assignation de nouvelles valeurs à une table. En lui faisant bloquer toutes les assignations, vous pouvez figer un objet :
local ImmutablePoint = {}
function ImmutablePoint.new(x, y)
local data = {x = x, y = y}
local proxy = {}
setmetatable(proxy, {
__index = data,
__newindex = function()
error("Cannot modify immutable object")
end
})
return proxy
endL'astuce consiste à utiliser une table proxy. Les données réelles résident dans data, tandis que l'utilisateur interagit avec le proxy vide.
Lors de la lecture, __index récupère les données de data. Lors de l'écriture, __newindex se déclenche à la place — et nous le bloquons :
local p = ImmutablePoint.new(10, 20)
print(p.x) -- 10 (la lecture fonctionne)
p.x = 50 -- Erreur : Impossible de modifier un objet immuableCe modèle est précieux pour les objets de configuration, les constantes mathématiques ou toute donnée qui ne devrait jamais changer après l'initialisation. L'objet reste entièrement lisible — vous ne pouvez tout simplement pas le modifier.
Défi
FacileConstruisons une classe ImmutableConfig qui crée des objets de configuration en lecture seule ! Une fois créés, ces objets de configuration doivent permettre la lecture de leurs paramètres mais bloquer complètement toute tentative de modification.
Vous organiserez votre code sur deux fichiers :
ImmutableConfig.lua: Créez un module qui produit des objets de configuration figés. Votre fonction.new(appName, version, maxUsers)doit stocker ces trois valeurs en interne, puis retourner une table proxy qui permet de lire tous les champs mais empêche toute modification. Lorsque quelqu'un tente de changer une valeur, elle doit afficherError: Config is read-onlyau lieu de lever une véritable erreur.main.lua: Donnez vie à votre ImmutableConfig ! Lisez trois entrées pour les valeurs de configuration, créez un objet de configuration, puis démontrez qu'il fonctionne correctement en :- Affichant les trois valeurs d'origine
- Tentant de modifier le champ
maxUsers(ce qui devrait déclencher le message d'erreur) - Affichant à nouveau
maxUserspour prouver qu'il n'a pas été réellement modifié
Vous recevrez trois entrées :
- Le nom de l'application (une chaîne de caractères)
- La version (une chaîne de caractères)
- Le nombre maximum d'utilisateurs (un nombre)
Votre sortie doit afficher les valeurs de configuration, l'erreur lors de la tentative de modification et la confirmation que la valeur est restée inchangée :
App: {appName}
Version: {version}
Max Users: {maxUsers}
Error: Config is read-only
Max Users: {maxUsers}Par exemple, si les entrées sont MyApp, 2.0, et 100, la sortie doit être :
App: MyApp
Version: 2.0
Max Users: 100
Error: Config is read-only
Max Users: 100Rappelez-vous : le modèle proxy utilise une table vide avec laquelle l'utilisateur interagit, tandis que les données réelles résident dans une table interne séparée. La métaméthode __index récupère les données de la table interne, et __newindex intercepte (et bloque) toute tentative d'écriture !
Aide-mémoire
La métaméthode __newindex intercepte les tentatives d'assignation de nouvelles valeurs à une table. En bloquant toutes les assignations, vous pouvez créer des objets immuables (en lecture seule) :
local ImmutablePoint = {}
function ImmutablePoint.new(x, y)
local data = {x = x, y = y}
local proxy = {}
setmetatable(proxy, {
__index = data,
__newindex = function()
error("Cannot modify immutable object")
end
})
return proxy
endLe proxy pattern (modèle de substitution) utilise une table vide (proxy) avec laquelle l'utilisateur interagit, tandis que les données réelles résident dans une table interne séparée (data) :
__indexrécupère les valeurs de la tabledatalors de la lecture__newindexintercepte les tentatives d'écriture et les bloque
local p = ImmutablePoint.new(10, 20)
print(p.x) -- 10 (la lecture fonctionne)
p.x = 50 -- Erreur : Cannot modify immutable objectCe modèle est utile pour les objets de configuration, les constantes mathématiques ou toute donnée qui doit rester inchangée après l'initialisation.
Essayez vous-même
-- Importer le module ImmutableConfig
local ImmutableConfig = require('ImmutableConfig')
-- Lire les entrées
local appName = io.read()
local version = io.read()
local maxUsers = tonumber(io.read())
-- TODO: Créer un objet de configuration en utilisant ImmutableConfig.new()
-- TODO: Afficher les trois valeurs originales au format :
-- App: {appName}
-- Version: {version}
-- Max Users: {maxUsers}
-- TODO: Tenter de modifier le champ maxUsers (cela devrait déclencher un message d'erreur)
-- TODO: Afficher à nouveau maxUsers pour prouver qu'il n'a pas été modifié
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Object Oriented Programming
1Le concept de « self »
Tables avec fonctions« self » expliciteLa syntaxe avec deux-pointsPoint vs deux-pointsRécapitulatif - Point mobile4Projet : Banque numérique
Configuration du projetMéthode de dépôt7Polymorphisme et Redéfinition
Redéfinition de méthodesAppeler des méthodes parentesDuck TypingInterface communeVérification du typeRécapitulatif - Rôles des employés10Projet : Gestionnaire de formes
Configuration du projetClasse Rectangle2Modèle de prototype de classe
Le concept de prototypeLiaison avec __indexLe constructeur :new()Initialisation des attributsInstances indépendantesRécapitulatif - Usine de voitures5Surcharge d'opérateurs en POO
Additionner des objetsSoustraire des objetsConcaténer des objetsComparer des objets (<, >)Récapitulatif - Calculs de portefeuille8Encapsulation
Conventions de nommageClosures pour la confidentialitéAccès via les ClosuresTables en lecture seuleLogique de validationRécapitulatif - Coffre-fort sécurisé11Patrons de conception (Lite)
Fonctions FactoryTable SingletonPatron IteratorObserver (Écouteur)Récapitulatif - Logger Factory3État et comportement des objets
Variables d'instanceMéthodes GetterMéthodes SetterPropriétés calculéesFormatage de chaînesVérifications d'égalitéRécapitulatif - Note de l'étudiant6Bases de l'héritage
Mise en place de l'héritageHériter des méthodesÉtendre le constructeurAjouter des méthodes enfantsPartagé vs UniqueRécapitulatif - Hiérarchie des formes