Lua Academy · Урок

Шаблоны модулей и рекомендации

Используйте шаблон local M = {} и аккуратно предоставляйте публичный программный интерфейс.

Урок 4 из 412 шагов

«Шаблоны модулей и рекомендации» — бесплатный урок Lua Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Lua Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Lua Academy содержит 4 уроков всего.

Шаблон M = {}

Универсальный шаблон модуля Lua: объявите локальную таблицу, заполните её и верните. Все открытые символы помещаются в M, а все закрытые вспомогательные элементы являются обычными локальными элементами. Этот подход понятен, минимален и работает повсюду.

-- 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.foo()), либо как локальные элементы. Использование локальных элементов немного быстрее; использование M.foo() позволяет пользователям переопределить M.foo, после чего внутренний вызов будет использовать переопределённую версию (изменение кода во время работы).

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

Шаблон одиночки

Модуль может работать как одиночка: он содержит изменяемое внутреннее состояние, общее для всех вызывающих объектов. Поскольку require кэширует модуль, все вызовы require("mod") получают один и тот же объект с одним и тем же состоянием.

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

Модуль как пространство имён

Используйте модуль только как пространство имён, чтобы не засорять глобальную таблицу. Объединяйте связанные константы и вспомогательные средства под одним именем, подобно пакету в других языках.

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

Модуль-фабрика

Такой модуль экспортирует функцию-фабрику вместо обычной таблицы. Фабрика создаёт и возвращает новые экземпляры с собственным закрытым состоянием. Это шаблон класса на уровне модуля.

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

Функция инициализации модуля

Некоторым модулям требуется конфигурация перед использованием. Предоставьте функцию M.init(config), которая сохранит конфигурацию в закрытом состоянии модуля. Это позволяет внедрять зависимости и упрощает тестирование.

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

Неизменяемый модуль

Предотвратите случайное изменение API модуля пользователями с помощью __newindex, блокируя все записи. Это особенно полезно для библиотечных модулей, где случайное изменение кода во время работы может нарушить их работу.

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)

Документирование с помощью LDoc

Распространённое соглашение для документирования модулей Lua — комментарии в стиле LDoc с префиксом ---. Язык не требует их использования, но эти комментарии позволяют инструментам автоматической генерации создавать документацию 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

Тестирование модулей

Тестируйте модуль, загрузив его и проверив работу каждой функции. Используйте простой исполнитель тестов или busted (тестовую платформу Lua). Храните тесты в отдельном файле, соответствующем пути модуля.

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

Объединение модулей

Сложные системы объединяют несколько модулей. Главная точка входа загружает подмодули и связывает их между собой. Такое разделение ответственности позволяет каждому модулю оставаться специализированным и упрощает независимое тестирование.

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

Быстрая проверка

Каково основное назначение шаблона модуля local M = {} ... return M?

Повторение: рекомендации по работе с модулями

Краткое содержание:

  • Всегда используйте local M = {} ... return M
  • Закрытые элементы = локальные элементы уровня файла; открытые элементы = поля M
  • Одиночка: require кэширует экземпляр модуля
  • Используйте функции-фабрики для состояния отдельных экземпляров
  • Замораживайте модули с помощью __newindex, чтобы предотвратить изменения
  • Проводите тестирование в отдельных файлах; документируйте код комментариями ---
Можно начать бесплатно

Изучай Lua с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
40
Уроки
159

Часто задаваемые вопросы

Урок «Шаблоны модулей и рекомендации» бесплатный?

Да — полный текст урока «Шаблоны модулей и рекомендации» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Lua Academy, подпишись на CoddyKit PRO. Курс Lua Academy содержит 4 уроков всего.

Чему я научусь в уроке «Шаблоны модулей и рекомендации»?

Используйте шаблон local M = {} и аккуратно предоставляйте публичный программный интерфейс. Ты практикуешь Lua Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Lua Academy?

Предыдущий опыт не требуется. Lua Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Шаблоны модулей и рекомендации»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Lua Academy?

Да. Каждый урок Lua Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Функция require
  2. Создание файла модуля
  3. package.path и package.cpath
  4. Шаблоны модулей и рекомендации
← Назад к Lua Academy