Skip to content

Lua模块与包

概述

模块化编程是现代软件开发的重要实践。Lua提供了强大的模块系统,允许开发者将代码组织成可重用的模块,并通过包管理机制来组织和分发这些模块。

1. 模块基础概念

1.1 什么是模块

模块是一个包含函数、变量和其他Lua对象的表。模块的主要目的是:

  • 代码重用 - 避免重复编写相同的代码
  • 命名空间 - 避免全局变量污染
  • 封装 - 隐藏内部实现细节
  • 组织 - 将相关功能组织在一起
lua
-- 简单的模块示例
local math_utils = {}

-- 公有函数
function math_utils.add(a, b)
    return a + b
end

function math_utils.multiply(a, b)
    return a * b
end

-- 私有函数(局部函数)
local function validate_number(n)
    return type(n) == "number"
end

-- 使用私有函数的公有函数
function math_utils.safe_divide(a, b)
    if not validate_number(a) or not validate_number(b) then
        error("参数必须是数字")
    end
    
    if b == 0 then
        error("除数不能为零")
    end
    
    return a / b
end

-- 模块常量
math_utils.PI = 3.14159265359
math_utils.E = 2.71828182846

return math_utils

1.2 模块的创建方式

lua
-- 方式1: 直接返回表
local string_utils = {}

function string_utils.trim(str)
    return string.match(str, "^%s*(.-)%s*$")
end

function string_utils.split(str, delimiter)
    local result = {}
    local pattern = "([^" .. delimiter .. "]+)"
    
    for match in string.gmatch(str, pattern) do
        table.insert(result, match)
    end
    
    return result
end

return string_utils

-- 方式2: 使用setmetatable创建模块
local array_utils = {}
array_utils.__index = array_utils

function array_utils.new(initial_data)
    local instance = initial_data or {}
    return setmetatable(instance, array_utils)
end

function array_utils:push(value)
    table.insert(self, value)
    return self
end

function array_utils:pop()
    return table.remove(self)
end

function array_utils:length()
    return #self
end

return array_utils

-- 方式3: 函数式模块
local function create_counter(initial_value)
    local count = initial_value or 0
    
    return {
        increment = function()
            count = count + 1
            return count
        end,
        
        decrement = function()
            count = count - 1
            return count
        end,
        
        get = function()
            return count
        end,
        
        reset = function()
            count = initial_value or 0
            return count
        end
    }
end

return create_counter

2. require函数和模块加载

2.1 require函数的使用

lua
-- 加载模块的基本方式
local math_utils = require("math_utils")
local result = math_utils.add(10, 20)
print(result)  -- 30

-- require的特性
-- 1. 模块只会被加载一次,后续require返回缓存的结果
local utils1 = require("math_utils")
local utils2 = require("math_utils")
print(utils1 == utils2)  -- true,是同一个对象

-- 2. require会在package.path中搜索模块文件
print("搜索路径:", package.path)

-- 3. 可以加载不同类型的模块
local json = require("json")        -- Lua模块
local socket = require("socket")    -- C模块(如果安装了)

-- 模块加载示例
local function demonstrate_module_loading()
    print("=== 模块加载演示 ===")
    
    -- 创建一个简单的模块文件内容
    local module_content = [[
-- simple_module.lua
local M = {}

M.version = "1.0.0"
M.name = "SimpleModule"

function M.greet(name)
    return "Hello, " .. (name or "World") .. "!"
end

function M.get_info()
    return {
        name = M.name,
        version = M.version,
        loaded_at = os.date()
    }
end

print("SimpleModule loaded at " .. os.date())

return M
]]
    
    -- 写入模块文件
    local file = io.open("simple_module.lua", "w")
    file:write(module_content)
    file:close()
    
    -- 加载模块
    local simple = require("simple_module")
    print("模块名称:", simple.name)
    print("模块版本:", simple.version)
    print(simple.greet("Lua"))
    
    local info = simple.get_info()
    for k, v in pairs(info) do
        print(k .. ":", v)
    end
    
    -- 清理
    os.remove("simple_module.lua")
end

demonstrate_module_loading()

2.2 模块搜索路径

lua
-- 模块搜索路径管理
local function module_path_management()
    print("\n=== 模块搜索路径 ===")
    
    -- 查看当前搜索路径
    print("Lua模块搜索路径:")
    for path in string.gmatch(package.path, "([^;]+)") do
        print("  " .. path)
    end
    
    print("\nC模块搜索路径:")
    for path in string.gmatch(package.cpath, "([^;]+)") do
        print("  " .. path)
    end
    
    -- 添加自定义搜索路径
    local function add_module_path(path)
        package.path = package.path .. ";" .. path .. "/?.lua"
        package.path = package.path .. ";" .. path .. "/?/init.lua"
    end
    
    -- 示例:添加自定义路径
    add_module_path("./my_modules")
    add_module_path("./lib")
    
    print("\n添加自定义路径后:")
    local paths = {}
    for path in string.gmatch(package.path, "([^;]+)") do
        table.insert(paths, path)
    end
    
    -- 只显示新添加的路径
    for i = #paths - 3, #paths do
        if paths[i] then
            print("  " .. paths[i])
        end
    end
end

module_path_management()

2.3 模块缓存机制

lua
-- 模块缓存机制
local function module_cache_management()
    print("\n=== 模块缓存机制 ===")
    
    -- 查看已加载的模块
    print("已加载的模块:")
    for name, module in pairs(package.loaded) do
        if type(name) == "string" and not string.match(name, "^_") then
            print("  " .. name .. ": " .. type(module))
        end
    end
    
    -- 创建测试模块
    local test_module_content = [[
local M = {}
M.load_time = os.time()
M.load_count = (M.load_count or 0) + 1
print("Test module loaded, count: " .. M.load_count)
return M
]]
    
    local file = io.open("test_cache.lua", "w")
    file:write(test_module_content)
    file:close()
    
    -- 第一次加载
    print("\n第一次加载:")
    local mod1 = require("test_cache")
    print("加载时间:", mod1.load_time)
    
    -- 第二次加载(从缓存)
    print("\n第二次加载:")
    local mod2 = require("test_cache")
    print("是否为同一对象:", mod1 == mod2)
    print("加载时间:", mod2.load_time)
    
    -- 强制重新加载模块
    print("\n强制重新加载:")
    package.loaded["test_cache"] = nil  -- 清除缓存
    local mod3 = require("test_cache")
    print("是否为同一对象:", mod1 == mod3)
    print("新的加载时间:", mod3.load_time)
    
    -- 清理
    os.remove("test_cache.lua")
    package.loaded["test_cache"] = nil
end

module_cache_management()

3. 高级模块模式

3.1 单例模式

lua
-- 单例模式模块
local function create_singleton_module()
    local singleton_content = [[
-- singleton.lua
local M = {}
local instance = nil

local function create_instance()
    return {
        id = math.random(1000000),
        created_at = os.time(),
        data = {}
    }
end

function M.get_instance()
    if not instance then
        instance = create_instance()
        print("创建新的单例实例,ID:", instance.id)
    end
    return instance
end

function M.reset()
    instance = nil
    print("单例实例已重置")
end

return M
]]
    
    local file = io.open("singleton.lua", "w")
    file:write(singleton_content)
    file:close()
    
    -- 测试单例模式
    print("\n=== 单例模式测试 ===")
    
    local singleton = require("singleton")
    
    local inst1 = singleton.get_instance()
    local inst2 = singleton.get_instance()
    
    print("实例1 ID:", inst1.id)
    print("实例2 ID:", inst2.id)
    print("是否为同一实例:", inst1 == inst2)
    
    -- 重置并获取新实例
    singleton.reset()
    local inst3 = singleton.get_instance()
    print("重置后实例3 ID:", inst3.id)
    print("与实例1是否相同:", inst1 == inst3)
    
    -- 清理
    os.remove("singleton.lua")
    package.loaded["singleton"] = nil
end

create_singleton_module()

3.2 工厂模式

lua
-- 工厂模式模块
local function create_factory_module()
    local factory_content = [[
-- shape_factory.lua
local M = {}

-- 形状类定义
local Shape = {}
Shape.__index = Shape

function Shape:new(type_name)
    local obj = {type = type_name}
    return setmetatable(obj, self)
end

function Shape:area()
    error("子类必须实现area方法")
end

function Shape:perimeter()
    error("子类必须实现perimeter方法")
end

-- 矩形类
local Rectangle = setmetatable({}, Shape)
Rectangle.__index = Rectangle

function Rectangle:new(width, height)
    local obj = Shape.new(self, "Rectangle")
    obj.width = width
    obj.height = height
    return obj
end

function Rectangle:area()
    return self.width * self.height
end

function Rectangle:perimeter()
    return 2 * (self.width + self.height)
end

-- 圆形类
local Circle = setmetatable({}, Shape)
Circle.__index = Circle

function Circle:new(radius)
    local obj = Shape.new(self, "Circle")
    obj.radius = radius
    return obj
end

function Circle:area()
    return math.pi * self.radius * self.radius
end

function Circle:perimeter()
    return 2 * math.pi * self.radius
end

-- 工厂方法
function M.create_shape(shape_type, ...)
    if shape_type == "rectangle" then
        return Rectangle:new(...)
    elseif shape_type == "circle" then
        return Circle:new(...)
    else
        error("未知的形状类型: " .. shape_type)
    end
end

function M.get_supported_shapes()
    return {"rectangle", "circle"}
end

return M
]]
    
    local file = io.open("shape_factory.lua", "w")
    file:write(factory_content)
    file:close()
    
    -- 测试工厂模式
    print("\n=== 工厂模式测试 ===")
    
    local factory = require("shape_factory")
    
    -- 创建不同的形状
    local rect = factory.create_shape("rectangle", 10, 5)
    local circle = factory.create_shape("circle", 3)
    
    print("矩形面积:", rect:area())
    print("矩形周长:", rect:perimeter())
    print("圆形面积:", string.format("%.2f", circle:area()))
    print("圆形周长:", string.format("%.2f", circle:perimeter()))
    
    print("支持的形状:", table.concat(factory.get_supported_shapes(), ", "))
    
    -- 清理
    os.remove("shape_factory.lua")
    package.loaded["shape_factory"] = nil
end

create_factory_module()

3.3 观察者模式

lua
-- 观察者模式模块
local function create_observer_module()
    local observer_content = [[
-- event_emitter.lua
local M = {}

function M.new()
    local emitter = {
        listeners = {}
    }
    
    function emitter:on(event, callback)
        if not self.listeners[event] then
            self.listeners[event] = {}
        end
        table.insert(self.listeners[event], callback)
        return self
    end
    
    function emitter:off(event, callback)
        if not self.listeners[event] then
            return self
        end
        
        for i, listener in ipairs(self.listeners[event]) do
            if listener == callback then
                table.remove(self.listeners[event], i)
                break
            end
        end
        return self
    end
    
    function emitter:emit(event, ...)
        if not self.listeners[event] then
            return self
        end
        
        for _, callback in ipairs(self.listeners[event]) do
            local success, err = pcall(callback, ...)
            if not success then
                print("事件处理器错误:", err)
            end
        end
        return self
    end
    
    function emitter:once(event, callback)
        local function wrapper(...)
            callback(...)
            self:off(event, wrapper)
        end
        return self:on(event, wrapper)
    end
    
    function emitter:listener_count(event)
        return self.listeners[event] and #self.listeners[event] or 0
    end
    
    return emitter
end

return M
]]
    
    local file = io.open("event_emitter.lua", "w")
    file:write(observer_content)
    file:close()
    
    -- 测试观察者模式
    print("\n=== 观察者模式测试 ===")
    
    local EventEmitter = require("event_emitter")
    local emitter = EventEmitter.new()
    
    -- 添加事件监听器
    emitter:on("user_login", function(username)
        print("用户登录:", username)
    end)
    
    emitter:on("user_login", function(username)
        print("记录日志: 用户", username, "在", os.date(), "登录")
    end)
    
    emitter:once("user_logout", function(username)
        print("用户", username, "退出登录(只触发一次)")
    end)
    
    -- 触发事件
    emitter:emit("user_login", "Alice")
    emitter:emit("user_logout", "Alice")
    emitter:emit("user_logout", "Alice")  -- 第二次不会触发
    
    print("user_login事件监听器数量:", emitter:listener_count("user_login"))
    print("user_logout事件监听器数量:", emitter:listener_count("user_logout"))
    
    -- 清理
    os.remove("event_emitter.lua")
    package.loaded["event_emitter"] = nil
end

create_observer_module()

4. 包管理

4.1 包的组织结构

lua
-- 包的组织结构示例
local function create_package_structure()
    print("\n=== 包结构示例 ===")
    
    -- 创建包目录结构
    local function create_directory(path)
        os.execute("mkdir " .. path .. " 2>nul")  -- Windows
        os.execute("mkdir -p " .. path .. " 2>/dev/null")  -- Unix
    end
    
    -- 创建目录
    create_directory("mypackage")
    create_directory("mypackage/utils")
    create_directory("mypackage/data")
    
    -- 主模块文件
    local main_module = [[
-- mypackage/init.lua
local M = {}

-- 加载子模块
M.utils = require("mypackage.utils")
M.data = require("mypackage.data")

M.version = "1.0.0"
M.name = "MyPackage"

function M.info()
    return {
        name = M.name,
        version = M.version,
        modules = {"utils", "data"}
    }
end

return M
]]
    
    -- 工具模块
    local utils_module = [[
-- mypackage/utils/init.lua
local M = {}

M.string = require("mypackage.utils.string")
M.array = require("mypackage.utils.array")

return M
]]
    
    -- 字符串工具
    local string_utils = [[
-- mypackage/utils/string.lua
local M = {}

function M.trim(str)
    return string.match(str, "^%s*(.-)%s*$")
end

function M.capitalize(str)
    return string.upper(string.sub(str, 1, 1)) .. string.lower(string.sub(str, 2))
end

function M.reverse(str)
    return string.reverse(str)
end

return M
]]
    
    -- 数组工具
    local array_utils = [[
-- mypackage/utils/array.lua
local M = {}

function M.map(array, func)
    local result = {}
    for i, v in ipairs(array) do
        result[i] = func(v)
    end
    return result
end

function M.filter(array, predicate)
    local result = {}
    for _, v in ipairs(array) do
        if predicate(v) then
            table.insert(result, v)
        end
    end
    return result
end

function M.reduce(array, func, initial)
    local result = initial
    for _, v in ipairs(array) do
        result = func(result, v)
    end
    return result
end

return M
]]
    
    -- 数据模块
    local data_module = [[
-- mypackage/data/init.lua
local M = {}

M.constants = {
    PI = 3.14159265359,
    E = 2.71828182846,
    GOLDEN_RATIO = 1.61803398875
}

M.config = {
    debug = false,
    max_retries = 3,
    timeout = 30
}

function M.get_constant(name)
    return M.constants[name]
end

function M.get_config(key)
    return M.config[key]
end

function M.set_config(key, value)
    M.config[key] = value
end

return M
]]
    
    -- 写入文件
    local files = {
        ["mypackage/init.lua"] = main_module,
        ["mypackage/utils/init.lua"] = utils_module,
        ["mypackage/utils/string.lua"] = string_utils,
        ["mypackage/utils/array.lua"] = array_utils,
        ["mypackage/data/init.lua"] = data_module
    }
    
    for filename, content in pairs(files) do
        local file = io.open(filename, "w")
        if file then
            file:write(content)
            file:close()
            print("创建文件:", filename)
        end
    end
    
    -- 测试包的使用
    print("\n测试包的使用:")
    local mypackage = require("mypackage")
    
    print("包信息:")
    local info = mypackage.info()
    for k, v in pairs(info) do
        if type(v) == "table" then
            print("  " .. k .. ":", table.concat(v, ", "))
        else
            print("  " .. k .. ":", v)
        end
    end
    
    -- 使用字符串工具
    local str = "  hello world  "
    print("\n字符串工具测试:")
    print("原始:", "'" .. str .. "'")
    print("trim:", "'" .. mypackage.utils.string.trim(str) .. "'")
    print("capitalize:", mypackage.utils.string.capitalize(str))
    print("reverse:", mypackage.utils.string.reverse("hello"))
    
    -- 使用数组工具
    local numbers = {1, 2, 3, 4, 5}
    print("\n数组工具测试:")
    print("原数组:", table.concat(numbers, ", "))
    
    local doubled = mypackage.utils.array.map(numbers, function(x) return x * 2 end)
    print("翻倍:", table.concat(doubled, ", "))
    
    local evens = mypackage.utils.array.filter(numbers, function(x) return x % 2 == 0 end)
    print("偶数:", table.concat(evens, ", "))
    
    local sum = mypackage.utils.array.reduce(numbers, function(acc, x) return acc + x end, 0)
    print("求和:", sum)
    
    -- 使用数据模块
    print("\n数据模块测试:")
    print("PI:", mypackage.data.get_constant("PI"))
    print("调试模式:", mypackage.data.get_config("debug"))
    
    mypackage.data.set_config("debug", true)
    print("设置后调试模式:", mypackage.data.get_config("debug"))
end

create_package_structure()

4.2 版本管理

lua
-- 版本管理示例
local function version_management_example()
    print("\n=== 版本管理示例 ===")
    
    local version_module = [[
-- version_manager.lua
local M = {}

-- 版本比较函数
function M.parse_version(version_string)
    local major, minor, patch = string.match(version_string, "(%d+)%.(%d+)%.(%d+)")
    if not major then
        error("无效的版本格式: " .. version_string)
    end
    
    return {
        major = tonumber(major),
        minor = tonumber(minor),
        patch = tonumber(patch),
        string = version_string
    }
end

function M.compare_versions(v1, v2)
    local version1 = type(v1) == "string" and M.parse_version(v1) or v1
    local version2 = type(v2) == "string" and M.parse_version(v2) or v2
    
    if version1.major ~= version2.major then
        return version1.major - version2.major
    end
    
    if version1.minor ~= version2.minor then
        return version1.minor - version2.minor
    end
    
    return version1.patch - version2.patch
end

function M.is_compatible(required_version, current_version)
    local req = M.parse_version(required_version)
    local cur = M.parse_version(current_version)
    
    -- 主版本号必须相同,次版本号和补丁版本号可以更高
    return cur.major == req.major and 
           (cur.minor > req.minor or 
            (cur.minor == req.minor and cur.patch >= req.patch))
end

-- 模块注册表
M.registry = {}

function M.register_module(name, version, module_obj)
    if not M.registry[name] then
        M.registry[name] = {}
    end
    
    M.registry[name][version] = {
        version = M.parse_version(version),
        module = module_obj,
        registered_at = os.time()
    }
end

function M.require_version(name, required_version)
    if not M.registry[name] then
        error("模块未注册: " .. name)
    end
    
    -- 查找兼容的版本
    local best_version = nil
    local best_info = nil
    
    for version_string, info in pairs(M.registry[name]) do
        if M.is_compatible(required_version, version_string) then
            if not best_version or M.compare_versions(version_string, best_version) > 0 then
                best_version = version_string
                best_info = info
            end
        end
    end
    
    if not best_info then
        error(string.format("找不到兼容版本的模块 %s (需要: %s)", name, required_version))
    end
    
    return best_info.module, best_version
end

function M.list_modules()
    local result = {}
    for name, versions in pairs(M.registry) do
        local version_list = {}
        for version in pairs(versions) do
            table.insert(version_list, version)
        end
        table.sort(version_list, function(a, b)
            return M.compare_versions(a, b) > 0
        end)
        result[name] = version_list
    end
    return result
end

return M
]]
    
    local file = io.open("version_manager.lua", "w")
    file:write(version_module)
    file:close()
    
    -- 测试版本管理
    local vm = require("version_manager")
    
    -- 注册不同版本的模块
    vm.register_module("utils", "1.0.0", {name = "Utils v1.0.0"})
    vm.register_module("utils", "1.1.0", {name = "Utils v1.1.0", new_feature = true})
    vm.register_module("utils", "2.0.0", {name = "Utils v2.0.0", breaking_change = true})
    
    vm.register_module("http", "0.9.0", {name = "HTTP v0.9.0"})
    vm.register_module("http", "1.0.0", {name = "HTTP v1.0.0"})
    
    -- 测试版本比较
    print("版本比较测试:")
    print("1.0.0 vs 1.1.0:", vm.compare_versions("1.0.0", "1.1.0"))
    print("2.0.0 vs 1.9.9:", vm.compare_versions("2.0.0", "1.9.9"))
    print("1.5.0 vs 1.5.0:", vm.compare_versions("1.5.0", "1.5.0"))
    
    -- 测试兼容性
    print("\n兼容性测试:")
    print("需要1.0.0,当前1.1.0:", vm.is_compatible("1.0.0", "1.1.0"))
    print("需要1.1.0,当前1.0.0:", vm.is_compatible("1.1.0", "1.0.0"))
    print("需要1.0.0,当前2.0.0:", vm.is_compatible("1.0.0", "2.0.0"))
    
    -- 测试版本要求
    print("\n版本要求测试:")
    local utils, version = vm.require_version("utils", "1.0.0")
    print("需要utils 1.0.0,获得:", utils.name, "版本:", version)
    
    local http, version = vm.require_version("http", "0.5.0")
    print("需要http 0.5.0,获得:", http.name, "版本:", version)
    
    -- 列出所有模块
    print("\n已注册的模块:")
    local modules = vm.list_modules()
    for name, versions in pairs(modules) do
        print("  " .. name .. ": " .. table.concat(versions, ", "))
    end
    
    -- 清理
    os.remove("version_manager.lua")
    package.loaded["version_manager"] = nil
end

version_management_example()

5. 实际应用示例

5.1 配置管理包

lua
-- 配置管理包示例
local function create_config_package()
    print("\n=== 配置管理包 ===")
    
    local config_package = [[
-- config_manager.lua
local M = {}

-- 默认配置
local default_config = {
    app = {
        name = "MyApp",
        version = "1.0.0",
        debug = false
    },
    database = {
        host = "localhost",
        port = 3306,
        timeout = 30
    },
    server = {
        port = 8080,
        max_connections = 100
    }
}

-- 当前配置
local current_config = {}

-- 深度复制函数
local function deep_copy(obj)
    if type(obj) ~= "table" then
        return obj
    end
    
    local copy = {}
    for k, v in pairs(obj) do
        copy[k] = deep_copy(v)
    end
    return copy
end

-- 深度合并函数
local function deep_merge(target, source)
    for k, v in pairs(source) do
        if type(v) == "table" and type(target[k]) == "table" then
            deep_merge(target[k], v)
        else
            target[k] = v
        end
    end
end

-- 初始化配置
function M.init(user_config)
    current_config = deep_copy(default_config)
    if user_config then
        deep_merge(current_config, user_config)
    end
end

-- 获取配置值
function M.get(path, default_value)
    local keys = {}
    for key in string.gmatch(path, "([^%.]+)") do
        table.insert(keys, key)
    end
    
    local current = current_config
    for _, key in ipairs(keys) do
        if type(current) ~= "table" or current[key] == nil then
            return default_value
        end
        current = current[key]
    end
    
    return current
end

-- 设置配置值
function M.set(path, value)
    local keys = {}
    for key in string.gmatch(path, "([^%.]+)") do
        table.insert(keys, key)
    end
    
    local current = current_config
    for i = 1, #keys - 1 do
        local key = keys[i]
        if type(current[key]) ~= "table" then
            current[key] = {}
        end
        current = current[key]
    end
    
    current[keys[#keys]] = value
end

-- 获取所有配置
function M.get_all()
    return deep_copy(current_config)
end

-- 重置为默认配置
function M.reset()
    current_config = deep_copy(default_config)
end

-- 从文件加载配置
function M.load_from_file(filename)
    local file = io.open(filename, "r")
    if not file then
        return false, "无法打开配置文件: " .. filename
    end
    
    local content = file:read("*a")
    file:close()
    
    -- 简单的JSON解析(仅支持基本格式)
    local success, config = pcall(load("return " .. content))
    if not success then
        return false, "配置文件格式错误"
    end
    
    deep_merge(current_config, config)
    return true
end

-- 保存配置到文件
function M.save_to_file(filename)
    local function serialize(obj, indent)
        indent = indent or 0
        local spaces = string.rep("  ", indent)
        
        if type(obj) == "table" then
            local result = "{\n"
            for k, v in pairs(obj) do
                local key = type(k) == "string" and string.format('"%s"', k) or tostring(k)
                result = result .. spaces .. "  " .. key .. " = " .. serialize(v, indent + 1) .. ",\n"
            end
            result = result .. spaces .. "}"
            return result
        elseif type(obj) == "string" then
            return string.format('"%s"', obj)
        else
            return tostring(obj)
        end
    end
    
    local file = io.open(filename, "w")
    if not file then
        return false, "无法创建配置文件: " .. filename
    end
    
    file:write(serialize(current_config))
    file:close()
    return true
end

-- 验证配置
function M.validate(schema)
    local function validate_value(value, rule)
        if rule.type and type(value) ~= rule.type then
            return false, "类型错误,期望 " .. rule.type .. ",实际 " .. type(value)
        end
        
        if rule.min and value < rule.min then
            return false, "值太小,最小值为 " .. rule.min
        end
        
        if rule.max and value > rule.max then
            return false, "值太大,最大值为 " .. rule.max
        end
        
        if rule.enum then
            local found = false
            for _, allowed in ipairs(rule.enum) do
                if value == allowed then
                    found = true
                    break
                end
            end
            if not found then
                return false, "值不在允许的范围内: " .. table.concat(rule.enum, ", ")
            end
        end
        
        return true
    end
    
    local function validate_object(obj, schema_obj, path)
        path = path or ""
        
        for key, rule in pairs(schema_obj) do
            local current_path = path == "" and key or path .. "." .. key
            local value = obj[key]
            
            if rule.required and value == nil then
                return false, "必填字段缺失: " .. current_path
            end
            
            if value ~= nil then
                if type(rule.properties) == "table" then
                    local success, err = validate_object(value, rule.properties, current_path)
                    if not success then
                        return false, err
                    end
                else
                    local success, err = validate_value(value, rule)
                    if not success then
                        return false, current_path .. ": " .. err
                    end
                end
            end
        end
        
        return true
    end
    
    return validate_object(current_config, schema)
end

-- 初始化默认配置
M.init()

return M
]]
    
    local file = io.open("config_manager.lua", "w")
    file:write(config_package)
    file:close()
    
    -- 测试配置管理包
    local config = require("config_manager")
    
    -- 基本使用
    print("应用名称:", config.get("app.name"))
    print("数据库端口:", config.get("database.port"))
    print("不存在的配置:", config.get("nonexistent.key", "默认值"))
    
    -- 设置配置
    config.set("app.debug", true)
    config.set("server.ssl.enabled", true)
    
    print("设置后的调试模式:", config.get("app.debug"))
    print("SSL启用状态:", config.get("server.ssl.enabled"))
    
    -- 配置验证
    local schema = {
        app = {
            properties = {
                name = {type = "string", required = true},
                version = {type = "string", required = true},
                debug = {type = "boolean"}
            }
        },
        database = {
            properties = {
                port = {type = "number", min = 1, max = 65535, required = true}
            }
        }
    }
    
    local valid, err = config.validate(schema)
    print("配置验证结果:", valid and "通过" or ("失败: " .. err))
    
    -- 保存配置
    local save_success = config.save_to_file("app_config.lua")
    if save_success then
        print("配置已保存到 app_config.lua")
        
        -- 读取保存的配置文件内容
        local file = io.open("app_config.lua", "r")
        local content = file:read("*a")
        file:close()
        print("保存的配置内容:")
        print(content)
        
        -- 清理
        os.remove("app_config.lua")
    end
    
    -- 清理
    os.remove("config_manager.lua")
    package.loaded["config_manager"] = nil
end

create_config_package()

5.2 日志记录包

lua
-- 日志记录包示例
local function create_logging_package()
    print("\n=== 日志记录包 ===")
    
    local logging_package = [[
-- logger.lua
local M = {}

-- 日志级别
M.LEVELS = {
    DEBUG = 1,
    INFO = 2,
    WARN = 3,
    ERROR = 4,
    FATAL = 5
}

local LEVEL_NAMES = {"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
local LEVEL_COLORS = {
    DEBUG = "\27[36m",  -- 青色
    INFO = "\27[32m",   -- 绿色
    WARN = "\27[33m",   -- 黄色
    ERROR = "\27[31m",  -- 红色
    FATAL = "\27[35m"   -- 紫色
}
local COLOR_RESET = "\27[0m"

-- 默认配置
local default_config = {
    level = M.LEVELS.INFO,
    format = "[%timestamp%] %level% %location% - %message%",
    timestamp_format = "%Y-%m-%d %H:%M:%S",
    output = "console",  -- console, file, both
    filename = "app.log",
    max_file_size = 10 * 1024 * 1024,  -- 10MB
    max_files = 5,
    colors = true
}

local config = {}
for k, v in pairs(default_config) do
    config[k] = v
end

-- 输出处理器
local outputs = {}

-- 控制台输出
outputs.console = function(formatted_message, level)
    local output = config.colors and 
        (LEVEL_COLORS[LEVEL_NAMES[level]] .. formatted_message .. COLOR_RESET) or 
        formatted_message
    print(output)
end

-- 文件输出
outputs.file = function(formatted_message, level)
    local file = io.open(config.filename, "a")
    if file then
        file:write(formatted_message .. "\n")
        file:close()
    end
end

-- 格式化消息
local function format_message(level, message, location)
    local formatted = config.format
    
    -- 替换占位符
    formatted = string.gsub(formatted, "%%timestamp%%", os.date(config.timestamp_format))
    formatted = string.gsub(formatted, "%%level%%", LEVEL_NAMES[level])
    formatted = string.gsub(formatted, "%%location%%", location or "")
    formatted = string.gsub(formatted, "%%message%%", message)
    
    return formatted
end

-- 获取调用位置信息
local function get_location()
    local info = debug.getinfo(4, "Sl")
    if info then
        local source = info.source
        if string.sub(source, 1, 1) == "@" then
            source = string.sub(source, 2)
        end
        return string.format("%s:%d", source, info.currentline or 0)
    end
    return "unknown"
end

-- 通用日志函数
local function log(level, message, ...)
    if level < config.level then
        return
    end
    
    -- 格式化消息
    if select("#", ...) > 0 then
        message = string.format(message, ...)
    end
    
    local location = get_location()
    local formatted = format_message(level, message, location)
    
    -- 输出到指定目标
    if config.output == "console" or config.output == "both" then
        outputs.console(formatted, level)
    end
    
    if config.output == "file" or config.output == "both" then
        outputs.file(formatted, level)
    end
end

-- 公共接口
function M.debug(message, ...)
    log(M.LEVELS.DEBUG, message, ...)
end

function M.info(message, ...)
    log(M.LEVELS.INFO, message, ...)
end

function M.warn(message, ...)
    log(M.LEVELS.WARN, message, ...)
end

function M.error(message, ...)
    log(M.LEVELS.ERROR, message, ...)
end

function M.fatal(message, ...)
    log(M.LEVELS.FATAL, message, ...)
end

-- 配置管理
function M.configure(new_config)
    for k, v in pairs(new_config) do
        if config[k] ~= nil then
            config[k] = v
        end
    end
end

function M.get_config()
    local result = {}
    for k, v in pairs(config) do
        result[k] = v
    end
    return result
end

-- 设置日志级别
function M.set_level(level)
    if type(level) == "string" then
        level = M.LEVELS[string.upper(level)]
    end
    if level then
        config.level = level
    end
end

-- 创建子记录器
function M.create_logger(name)
    local logger = {}
    
    local function child_log(level, message, ...)
        if level < config.level then
            return
        end
        
        if select("#", ...) > 0 then
            message = string.format(message, ...)
        end
        
        local prefixed_message = "[" .. name .. "] " .. message
        local location = get_location()
        local formatted = format_message(level, prefixed_message, location)
        
        if config.output == "console" or config.output == "both" then
            outputs.console(formatted, level)
        end
        
        if config.output == "file" or config.output == "both" then
            outputs.file(formatted, level)
        end
    end
    
    logger.debug = function(message, ...) child_log(M.LEVELS.DEBUG, message, ...) end
    logger.info = function(message, ...) child_log(M.LEVELS.INFO, message, ...) end
    logger.warn = function(message, ...) child_log(M.LEVELS.WARN, message, ...) end
    logger.error = function(message, ...) child_log(M.LEVELS.ERROR, message, ...) end
    logger.fatal = function(message, ...) child_log(M.LEVELS.FATAL, message, ...) end
    
    return logger
end

return M
]]
    
    local file = io.open("logger.lua", "w")
    file:write(logging_package)
    file:close()
    
    -- 测试日志包
    local logger = require("logger")
    
    -- 基本日志记录
    logger.debug("这是调试信息")
    logger.info("应用程序启动")
    logger.warn("这是警告信息: %s", "内存使用率高")
    logger.error("发生错误: %s", "文件未找到")
    
    -- 配置日志
    logger.configure({
        level = logger.LEVELS.DEBUG,
        format = "[%timestamp%] [%level%] %message%",
        colors = false
    })
    
    print("\n配置更改后:")
    logger.debug("现在可以看到调试信息了")
    logger.info("格式已更改")
    
    -- 创建子记录器
    local db_logger = logger.create_logger("DATABASE")
    local http_logger = logger.create_logger("HTTP")
    
    print("\n子记录器测试:")
    db_logger.info("数据库连接已建立")
    http_logger.warn("HTTP请求超时")
    
    -- 文件输出测试
    logger.configure({output = "file", filename = "test.log"})
    logger.info("这条消息将写入文件")
    
    -- 检查文件是否创建
    local log_file = io.open("test.log", "r")
    if log_file then
        print("\n日志文件内容:")
        print(log_file:read("*a"))
        log_file:close()
        os.remove("test.log")
    end
    
    -- 清理
    os.remove("logger.lua")
    package.loaded["logger"] = nil
end

create_logging_package()

6. 总结

Lua的模块与包系统为代码组织和重用提供了强大的支持:

核心要点

  • 模块化设计:将相关功能组织到独立的模块中
  • 包管理:使用LuaRocks进行依赖管理
  • 版本控制:合理的版本策略确保兼容性
  • 性能优化:模块缓存和延迟加载提升性能
  • 最佳实践:遵循命名规范和文档标准

实际应用

  • 构建可维护的大型应用
  • 创建可重用的库和工具
  • 实现插件系统和扩展机制
  • 管理项目依赖关系

掌握模块与包系统是Lua高级编程的重要基础。

基于 MIT 许可发布