0Pricing
Lua Academy · Leçon

Modèles de modules et bonnes pratiques

Utilisez le modèle local M = {} et exposez proprement une API publique.

Modèles de modules et bonnes pratiques est une leçon Lua Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Lua Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Lua Academy comprend 4 leçons au total.

Le modèle M = {}

Le modèle universel des modules Lua consiste à déclarer une table locale, à la remplir, puis à la renvoyer. Tous les symboles publics vont dans M ; tous les utilitaires privés sont de simples variables locales. Ce modèle est clair, minimal et fonctionne partout.

-- The canonical module pattern
local M = {}

-- Private helper (not exported)
local function validate(x)
  return type(x) == "number" and x >= 0
end

-- Public API
function M.sqrt(x)
  assert(validate(x), "expected non-negative number")
  return math.sqrt(x)
end

M.PI = math.pi

return M

Module autoréférencé

Dans un module, les fonctions peuvent appeler d'autres fonctions du module soit par leur nom (M.foo()), soit en tant que variables locales. L'utilisation de variables locales est légèrement plus rapide ; l'utilisation de M.foo() permet aux utilisateurs de remplacer M.foo et de faire en sorte que l'appel interne utilise ce remplacement (modification à chaud).

local M = {}

-- Option A: use M.foo inside (allows override)
function M.double(n) return M.multiply(n, 2) end
function M.multiply(a, b) return a * b end

-- Option B: use local function (faster, no override)
local function mul(a, b) return a * b end
function M.triple(n) return mul(n, 3) end

return M

Modèle singleton

Un module peut se comporter comme un singleton : il possède un état interne modifiable partagé par tous les appelants. Comme require met le module en cache, tous les appels à require("mod") obtiennent le même objet avec le même état.

-- config.lua (singleton)
local M = {}
local _config = {env="dev", logLevel="info"}

function M.set(key, val)
  _config[key] = val
end

function M.get(key)
  return _config[key]
end

function M.load(t)
  for k,v in pairs(t) do _config[k]=v end
end

return M

-- All callers share the same config:

Module comme espace de noms

Utilisez un module uniquement comme espace de noms afin d'éviter de polluer la table globale. Regroupez les constantes et les utilitaires associés sous un même nom, comme un paquet dans d'autres langages.

-- constants.lua
local M = {
  HTTP = {
    OK=200, CREATED=201, NO_CONTENT=204,
    BAD_REQUEST=400, UNAUTHORIZED=401,
    NOT_FOUND=404, SERVER_ERROR=500,
  },
  COLORS = {RED="#FF0000", GREEN="#00FF00", BLUE="#0000FF"},
  MAX_RETRIES = 3,
  TIMEOUT_SEC = 30,
}
return M

-- local C = require("constants")
-- if status == C.HTTP.NOT_FOUND then ...

Module fabrique

Il s'agit d'un module qui exporte une fonction fabrique au lieu d'une simple table. La fabrique crée et renvoie de nouvelles instances possédant leur propre état privé. Il s'agit du modèle de classe appliqué au niveau du module.

-- logger.lua
local M = {}

function M.new(name, level)
  level = level or "info"
  local levels = {debug=1,info=2,warn=3,error=4}
  local self = {}
  
  function self.log(msgLevel, msg)
    if levels[msgLevel] >= levels[level] then
      print(string.format("[%s][%s] %s", name, msgLevel:upper(), msg))
    end
  end
  
  function self.info(msg)  self.log("info",  msg) end
  function self.warn(msg)  self.log("warn",  msg) end
  function self.error(msg) self.log("error", msg) end
  
  return self
end

return M

Fonction d'initialisation du module

Certains modules nécessitent une configuration avant leur utilisation. Fournissez une fonction M.init(config) qui stocke la configuration dans l'état privé du module. Cela permet l'injection de dépendances et facilite les tests.

-- db.lua
local M = {}
local pool = nil

function M.init(config)
  pool = {
    host = config.host or "localhost",
    port = config.port or 5432,
    connections = {},
  }
  print("DB initialized:", pool.host, pool.port)
end

function M.query(sql)
  assert(pool, "call db.init() first")
  -- ... execute query
  return {}
end

return M

Module immuable

Empêchez les utilisateurs de modifier accidentellement l'API du module en utilisant __newindex pour bloquer toutes les écritures. Cela est particulièrement utile pour les modules de bibliothèque, car une modification à chaud accidentelle pourrait provoquer des dysfonctionnements.

local function freeze(t)
  return setmetatable({}, {
    __index = t,
    __newindex = function(_, k, _)
      error("module is read-only, cannot set: " .. tostring(k), 2)
    end
  })
end

local M = {}
function M.add(a, b) return a + b end
function M.sub(a, b) return a - b end

return freeze(M)

Documenter avec LDoc

Une convention courante pour documenter les modules Lua consiste à utiliser des commentaires de style LDoc précédés de ---. Bien que le langage ne l'impose pas, ces commentaires permettent aux outils de génération de documentation de produire automatiquement la documentation de l'API.

--- A utility module for string operations.
-- @module stringutils
local M = {}

--- Trim leading and trailing whitespace.
-- @param s string The input string.
-- @return string The trimmed string.
function M.trim(s)
  return s:match("^%s*(.-)%s*$")
end

--- Count occurrences of a substring.
-- @param str string The string to search.
-- @param sub string The substring to count.
-- @return number Count of occurrences.
function M.count(str, sub)
  local _, n = str:gsub(sub, "")
  return n
end

return M

Tester les modules

Testez un module en le requérant et en essayant chacune de ses fonctions. Utilisez un exécuteur de tests simple ou busted (le cadre de test de Lua). Conservez les tests dans un fichier séparé qui reproduit le chemin du module.

-- test/test_stringutils.lua
local su = require("stringutils")

local function test(name, fn)
  local ok, err = pcall(fn)
  if ok then print("[PASS] " .. name)
  else   print("[FAIL] " .. name .. ": " .. err)
  end
end

test("trim removes spaces", function()
  assert(su.trim("  hello  ") == "hello")
end)

test("trim empty string", function()
  assert(su.trim("") == "")
end)

test("count occurrences", function()
  assert(su.count("banana", "a") == 3)
end)

Composer des modules

Les systèmes complexes combinent plusieurs modules. Un point d'entrée principal requiert les sous-modules et les relie entre eux. Cette séparation des responsabilités permet à chaque module de rester ciblé et d'être testé indépendamment.

-- app.lua (main entry point)
local config = require("config")
local db     = require("db")
local server = require("server")

-- Configure from environment
config.load({
  dbHost = os.getenv("DB_HOST") or "localhost",
  port   = tonumber(os.getenv("PORT")) or 8080,
})

-- Wire modules together
db.init({host=config.get("dbHost"), port=5432})
server.init({port=config.get("port"), db=db})
server.start()

Vérification rapide

Quel est l'objectif principal du modèle de module local M = {} ... return M ?

Récapitulatif : bonnes pratiques pour les modules

Résumé :

  • Utilisez toujours local M = {} ... return M
  • Privé = variables locales au niveau du fichier ; public = champs de M
  • Singleton : require met en cache l'instance du module
  • Utilisez des fonctions fabriques pour un état propre à chaque instance
  • Figez les modules avec __newindex pour empêcher toute modification
  • Testez dans des fichiers séparés ; documentez avec des commentaires ---

Questions Fréquemment Posées

La leçon « Modèles de modules et bonnes pratiques » est-elle gratuite ?

Oui — le texte complet de « Modèles de modules et bonnes pratiques » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Lua Academy, passe à CoddyKit PRO. Le cours Lua Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Modèles de modules et bonnes pratiques » ?

Utilisez le modèle local M = {} et exposez proprement une API publique. Tu pratiques Lua Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Lua Academy ?

Aucune expérience préalable n'est requise. Lua Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.

Combien de temps prend la leçon « Modèles de modules et bonnes pratiques » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Lua Academy ?

Oui. Chaque leçon Lua Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. La fonction require
  2. Écrire un fichier de module
  3. package.path et package.cpath
  4. Modèles de modules et bonnes pratiques
← Retour à Lua Academy