Padrões e boas práticas para módulos
Use o padrão local M = {} e exponha a API pública de forma organizada.
Padrões e boas práticas para módulos é uma aula grátis de Lua Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Lua Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Lua Academy inclui 4 aulas no total.
O padrão M = {}
O padrão universal de módulo Lua: declare uma tabela local, preencha-a e retorne-a. Todos os símbolos públicos vão em M; todos os auxiliares privados são variáveis locais comuns. Isso é claro, minimalista e funciona em qualquer lugar.
-- 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 MMódulo autorreferente
Dentro de um módulo, as funções podem chamar outras funções do módulo pelo nome (M.foo()) ou como variáveis locais. Usar variáveis locais é um pouco mais rápido; usar M.foo() permite que os usuários substituam M.foo e faz com que a chamada interna use a substituição (alteração dinâmica).
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 MPadrão singleton
Um módulo pode atuar como um singleton: ele tem um estado interno mutável compartilhado por todos os chamadores. Como require armazena o módulo em cache, todas as chamadas a require("mod") obtêm o mesmo objeto com o mesmo estado.
-- 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:Módulo como espaço de nomes
Use um módulo exclusivamente como espaço de nomes para evitar poluir a tabela global. Agrupe constantes e utilitários relacionados sob um único nome, como um pacote em outras linguagens.
-- 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 ...Módulo fábrica
Um módulo que exporta uma função fábrica em vez de uma tabela comum. A fábrica cria e retorna novas instâncias com seu próprio estado privado. Esse é o padrão de classe no nível do módulo.
-- 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 MFunção de inicialização do módulo
Alguns módulos exigem configuração antes do uso. Forneça uma função M.init(config) que armazene a configuração no estado privado do módulo. Isso permite a injeção de dependências e a testabilidade.
-- 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 MMódulo imutável
Impeça que os usuários modifiquem acidentalmente a API do módulo usando __newindex para bloquear todas as escritas. Isso é especialmente útil para módulos de biblioteca nos quais alterações dinâmicas acidentais poderiam causar problemas.
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)Documentando com LDoc
Uma convenção comum para documentar módulos Lua são comentários no estilo LDoc com o prefixo ---. Embora não sejam impostos pela linguagem, esses comentários permitem que ferramentas de geração de documentação produzam automaticamente documentos da 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 MTestando módulos
Teste um módulo requerendo-o e exercitando cada função. Use um executor de testes simples ou busted (a estrutura de testes de Lua). Mantenha os testes em um arquivo separado que corresponda ao caminho do módulo.
-- 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)Compondo módulos
Sistemas complexos combinam vários módulos. Um ponto de entrada principal requer e conecta os submódulos. Essa separação de responsabilidades mantém cada módulo concentrado e testável de forma independente.
-- 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ção rápida
Qual é a finalidade principal do padrão de módulo local M = {} ... return M?
Recapitulação: práticas recomendadas para módulos
Resumo:
- Sempre use
local M = {} ... return M - Privado = variáveis locais no nível do arquivo; público = campos de M
- Singleton: require armazena a instância do módulo em cache
- Use funções fábrica para estados por instância
- Congele módulos com __newindex para impedir modificações
- Teste em arquivos separados; documente com comentários ---
Perguntas Frequentes
A aula “Padrões e boas práticas para módulos” é grátis?
Sim — o texto completo de “Padrões e boas práticas para módulos” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Lua Academy, atualize para CoddyKit PRO. O curso de Lua Academy inclui 4 aulas no total.
O que vou aprender em “Padrões e boas práticas para módulos”?
Use o padrão local M = {} e exponha a API pública de forma organizada. Você pratica Lua Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Lua Academy?
Nenhuma experiência prévia é necessária. Lua Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Padrões e boas práticas para módulos”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Lua Academy?
Sim. Cada aula de Lua Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- A função require
- Escrevendo um arquivo de módulo
- package.path e package.cpath
- Padrões e boas práticas para módulos