Menu
Coddy logo textTech

Table Singleton

Fait partie de la section Object Oriented Programming du Journey Lua de Coddy — leçon 63 sur 70.

Parfois, vous avez besoin d'exactement une seule instance de quelque chose dans l'ensemble de votre programme — un gestionnaire de configuration, un système de journalisation ou des paramètres de jeu. Créer plusieurs copies causerait des problèmes : laquelle contient les données correctes ? Le pattern Singleton garantit qu'une seule instance existe.

En Lua, implémenter un Singleton est étonnamment simple grâce au fonctionnement des modules. Lorsque vous require un module, Lua met le résultat en cache. Les appels suivants à require retournent la table mise en cache, et non une nouvelle.

-- GameConfig.lua
local GameConfig = {
    volume = 80,
    difficulty = "normal"
}

function GameConfig:setVolume(v)
    self.volume = v
end

return GameConfig

Maintenant, n'importe où dans votre programme :

local config1 = require("GameConfig")
local config2 = require("GameConfig")

config1:setVolume(50)
print(config2.volume)  -- 50 (même table !)

À la fois config1 et config2 font référence à la même table exacte. Les modifications effectuées via une variable sont visibles via l'autre. Cela se produit automatiquement car le require de Lua stocke la valeur retournée dans package.loaded et la réutilise.

Contrairement au pattern Factory qui crée de nouveaux objets, un Singleton garantit un état partagé à travers l'ensemble de votre application. C'est idéal pour les paramètres globaux auxquels plusieurs parties de votre code doivent accéder et qu'elles doivent modifier de manière cohérente.

challenge icon

Défi

Facile

Construisons un gestionnaire de paramètres de jeu en utilisant le motif Singleton ! Vous allez créer un module de configuration qui garantit que chaque partie de votre programme accède exactement aux mêmes paramètres, peu importe le nombre de fois qu'il est requis.

Vous organiserez votre code sur deux fichiers :

  • GameSettings.lua : Créez un module Singleton qui stocke la configuration du jeu. Votre table de paramètres doit inclure musicVolume (commençant à 70), sfxVolume (commençant à 100) et difficulty (commençant à "normal"). Ajoutez trois méthodes : :setMusicVolume(value), :setSfxVolume(value) et :setDifficulty(level) pour modifier ces paramètres. N'oubliez pas de retourner la table à la fin du module !
  • main.lua : Démontrez que le Singleton fonctionne en appelant votre module GameSettings deux fois dans des variables différentes. Utilisez la première variable pour changer le volume de la musique et la difficulté. Utilisez ensuite la seconde variable pour afficher les trois paramètres ; si le Singleton fonctionne correctement, les deux variables font référence à la même table, de sorte que les modifications effectuées via l'une sont visibles via l'autre.

Vous recevrez deux entrées :

  1. Nouveau volume de musique (un nombre)
  2. Nouveau niveau de difficulté (une chaîne de caractères comme "easy" ou "hard")

Après avoir modifié les paramètres via la première variable, affichez les trois paramètres en utilisant la seconde variable, chacun sur sa propre ligne dans cet ordre : volume de la musique, volume des effets sonores (sfx), puis difficulté.

Par exemple, si les entrées sont 50 et hard, la sortie devrait être :

50
100
hard

Si les entrées sont 25 et easy, la sortie devrait être :

25
100
easy

Remarquez que le volume des effets sonores reste à sa valeur par défaut de 100 puisque nous ne l'avons jamais modifié, mais le volume de la musique et la difficulté reflètent les changements effectués via la première variable, prouvant que les deux variables pointent vers la même instance Singleton !

Aide-mémoire

Le pattern Singleton garantit qu'une seule instance d'un objet existe dans l'ensemble de votre programme. C'est utile pour les gestionnaires de configuration, les systèmes de journalisation ou les paramètres de jeu où plusieurs instances causeraient une incohérence des données.

En Lua, le pattern Singleton est implémenté naturellement via le système de modules. Lorsque vous utilisez require pour un module, Lua met le résultat en cache dans package.loaded. Les appels suivants à require renvoient la même table mise en cache, et non une nouvelle.

Création d'un module Singleton :

-- GameConfig.lua
local GameConfig = {
    volume = 80,
    difficulty = "normal"
}

function GameConfig:setVolume(v)
    self.volume = v
end

return GameConfig

Utilisation du Singleton :

local config1 = require("GameConfig")
local config2 = require("GameConfig")

config1:setVolume(50)
print(config2.volume)  -- 50 (même table !)

config1 et config2 font tous deux référence à la même table exacte. Les modifications effectuées via une variable sont immédiatement visibles via l'autre, garantissant un état partagé dans toute votre application.

Le pattern Singleton diffère du pattern Factory : alors que Factory crée de nouveaux objets, Singleton garantit une instance partagée unique avec un état cohérent.

Essayez vous-même

-- main.lua - Démontrer le patron de conception Singleton

-- Lire les entrées
local newMusicVolume = tonumber(io.read())
local newDifficulty = io.read()

-- TODO: Charger le module GameSettings dans une variable (par ex., settings1)

-- TODO: Charger le module GameSettings À NOUVEAU dans une variable DIFFÉRENTE (par ex., settings2)
-- (Si le Singleton fonctionne correctement, les deux variables référencent la même table !)

-- TODO: Utiliser la PREMIÈRE variable pour :
--   - Définir le volume de la musique sur newMusicVolume
--   - Définir la difficulté sur newDifficulty

-- TODO: Utiliser la SECONDE variable pour afficher les paramètres
-- Afficher chacun sur sa propre ligne : musicVolume, sfxVolume, difficulty
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