0Pricing
Lua Academy · Lección

Patrones de módulos y buenas prácticas

Utilice el patrón local M = {} y exponga claramente una API pública.

Patrones de módulos y buenas prácticas es una lección gratuita de Lua Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Lua Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Lua Academy incluye 4 lecciones en total.

El patrón M = {}

El patrón universal de módulos de Lua consiste en declarar una tabla local, rellenarla y devolverla. Todos los símbolos públicos se incluyen en M; todas las funciones auxiliares privadas son variables locales normales. Es claro, minimalista y funciona en cualquier entorno.

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

Dentro de un módulo, las funciones pueden llamar a otras funciones del módulo por su nombre (M.foo()) o como variables locales. Usar variables locales es ligeramente más rápido; usar M.foo() permite que los usuarios sustituyan M.foo y que la llamada interna use la sustitución (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

Patrón Singleton

Un módulo puede actuar como un singleton: tiene un estado interno mutable que comparten todos los usuarios. Como require almacena el módulo en caché, todas las llamadas a require("mod") obtienen el mismo objeto con el mismo 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 espacio de nombres

Use un módulo únicamente como espacio de nombres para evitar contaminar la tabla global. Agrupe constantes y utilidades relacionadas bajo un mismo nombre, como un paquete en otros lenguajes.

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

Es un módulo que exporta una función fábrica en lugar de una tabla simple. La fábrica crea y devuelve nuevas instancias con su propio estado privado. Este es el patrón de clases aplicado al nivel del 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

Función de inicialización del módulo

Algunos módulos requieren configuración antes de utilizarse. Proporcione una función M.init(config) que almacene la configuración en el estado privado del módulo. Esto permite la inyección de dependencias y facilita las pruebas.

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

Evite que los usuarios modifiquen accidentalmente la API del módulo mediante __newindex para bloquear todas las escrituras. Esto resulta especialmente útil en módulos de bibliotecas, donde un monkey patching accidental podría causar errores.

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)

Documentación con LDoc

Una convención habitual para documentar módulos de Lua son los comentarios con estilo LDoc y el prefijo ---. Aunque el lenguaje no los exige, estos comentarios permiten que las herramientas de generación de documentación produzcan automáticamente la documentación de la 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

Pruebas de módulos

Pruebe un módulo requiriéndolo y ejecutando cada una de sus funciones. Use un ejecutor de pruebas sencillo o busted (el framework de pruebas de Lua). Mantenga las pruebas en un archivo separado que refleje la ruta del 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)

Composición de módulos

Los sistemas complejos combinan varios módulos. Un punto de entrada principal requiere los submódulos y los conecta entre sí. Esta separación de responsabilidades mantiene cada módulo centrado y permite probarlo de forma independiente.

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

Comprobación rápida

¿Cuál es el objetivo principal del patrón de módulos local M = {} ... return M?

Resumen: prácticas recomendadas para módulos

Resumen:

  • Use siempre local M = {} ... return M
  • Privado = variables locales del archivo; público = campos de M
  • Singleton: require almacena en caché la instancia del módulo
  • Use funciones fábrica para el estado independiente de cada instancia
  • Congele los módulos con __newindex para evitar modificaciones
  • Pruebe en archivos separados y documente con comentarios ---

Preguntas frecuentes

¿La lección «Patrones de módulos y buenas prácticas» es gratis?

Sí — el texto completo de «Patrones de módulos y buenas prácticas» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Lua Academy, actualiza a CoddyKit PRO. El curso de Lua Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Patrones de módulos y buenas prácticas»?

Utilice el patrón local M = {} y exponga claramente una API pública. Practicas Lua Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Lua Academy?

No se requiere experiencia previa. Lua Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Patrones de módulos y buenas prácticas»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Lua Academy?

Sí. Cada lección de Lua Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. La función require
  2. Escritura de un archivo de módulo
  3. package.path y package.cpath
  4. Patrones de módulos y buenas prácticas
← Volver a Lua Academy