Menu
Coddy logo textTech

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
end

L'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 immuable

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

challenge icon

Défi

Facile

Construisons 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 afficher Error: Config is read-only au 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 maxUsers pour prouver qu'il n'a pas été réellement modifié

Vous recevrez trois entrées :

  1. Le nom de l'application (une chaîne de caractères)
  2. La version (une chaîne de caractères)
  3. 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: 100

Rappelez-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
end

Le 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) :

  • __index récupère les valeurs de la table data lors de la lecture
  • __newindex intercepte 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 object

Ce 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é
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 Object Oriented Programming