Lua Academy · Lezione

Pattern e buone pratiche per i moduli

Utilizzi il pattern local M = {} ed esponga in modo ordinato l'API pubblica.

Lezione 4 di 412 passaggi

Pattern e buone pratiche per i moduli è una lezione Lua Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Lua Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Lua Academy include 4 lezioni in totale.

Il modello M = {}

Il modello universale per i moduli Lua: dichiarare una tabella locale, popolarla e restituirla. Tutti i simboli pubblici vengono inseriti in M; tutti gli helper privati sono semplici variabili locali. È chiaro, minimale e funziona ovunque.

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

Modulo autoreferenziale

All'interno di un modulo, le funzioni possono chiamare altre funzioni del modulo tramite il nome (M.foo()) oppure come variabili locali. L'uso delle variabili locali è leggermente più veloce; l'uso di M.foo() consente agli utenti di sovrascrivere M.foo e di fare in modo che la chiamata interna utilizzi la sovrascrittura (monkey patching).

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

Modello Singleton

Un modulo può comportarsi come un singleton: possiede uno stato interno mutabile condiviso da tutti gli utenti. Poiché require memorizza il modulo nella cache, tutte le chiamate a require("mod") ricevono lo stesso oggetto con lo stesso stato.

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

Modulo come spazio dei nomi

Usi un modulo esclusivamente come spazio dei nomi per evitare di inquinare la tabella globale. Raggruppi costanti e utility correlate sotto un unico nome, come un package negli altri linguaggi.

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

Modulo factory

Un modulo che esporta una funzione factory invece di una semplice tabella. La factory crea e restituisce nuove istanze con il proprio stato privato. Questo è il modello delle classi applicato a livello di modulo.

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

Funzione di inizializzazione del modulo

Alcuni moduli richiedono una configurazione prima dell'uso. Fornisca una funzione M.init(config) che memorizzi la configurazione nello stato privato del modulo. Questo consente l'iniezione delle dipendenze e facilita i test.

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

Modulo immutabile

Impedisca agli utenti di modificare accidentalmente l'API del modulo usando __newindex per bloccare tutte le scritture. È particolarmente utile per i moduli di libreria, nei quali il monkey patching accidentale potrebbe causare malfunzionamenti.

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)

Documentazione con LDoc

Una convenzione comune per documentare i moduli Lua consiste nell'usare commenti in stile LDoc con il prefisso ---. Sebbene il linguaggio non li imponga, questi commenti consentono agli strumenti di generazione della documentazione di produrre automaticamente la documentazione dell'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

Test dei moduli

Testi un modulo richiedendolo e verificando ogni funzione. Usi un semplice test runner oppure busted (il framework di test per Lua). Mantenga i test in un file separato che rispecchi il percorso del modulo.

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

Composizione dei moduli

I sistemi complessi combinano più moduli. Un punto di ingresso principale richiede e collega i sottomoduli. Questa separazione delle responsabilità mantiene ogni modulo focalizzato e testabile in modo indipendente.

-- 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()

Verifica rapida

Qual è lo scopo principale del modello di modulo local M = {} ... return M?

Riepilogo: buone pratiche per i moduli

Riepilogo:

  • Usi sempre local M = {} ... return M
  • Privato = variabili locali a livello di file; pubblico = campi di M
  • Il require memorizza nella cache l'istanza del singleton
  • Usi funzioni factory per lo stato specifico di ogni istanza
  • Blocchi le modifiche ai moduli con __newindex
  • Esegua i test in file separati; documenti il codice con commenti ---
Gratis per iniziare

Impara Lua con un tutor IA — gratis

Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.

Corsi
40
Lezioni
159

Domande Frequenti

La lezione «Pattern e buone pratiche per i moduli» è gratuita?

Sì — il testo completo di «Pattern e buone pratiche per i moduli» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Lua Academy, passa a CoddyKit PRO. Il corso Lua Academy include 4 lezioni in totale.

Cosa imparerò in «Pattern e buone pratiche per i moduli»?

Utilizzi il pattern local M = {} ed esponga in modo ordinato l'API pubblica. Eserciti Lua Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Lua Academy?

Non è richiesta alcuna esperienza precedente. Lua Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Pattern e buone pratiche per i moduli»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Lua Academy?

Sì. Ogni lezione Lua Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. La funzione require
  2. Scrittura di un file di modulo
  3. package.path e package.cpath
  4. Pattern e buone pratiche per i moduli
← Torna a Lua Academy