0Pricing
Lua Academy · Aula

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 M

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

Padrã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 M

Funçã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 M

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

Testando 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

  1. A função require
  2. Escrevendo um arquivo de módulo
  3. package.path e package.cpath
  4. Padrões e boas práticas para módulos
← Voltar para Lua Academy