创建一组相互依赖的Lua文件,而不会影响全局命名空间

Phr*_*ogz 5 lua

tl; dr:什么设计模式允许您将Lua代码分割为多个文件,这些文件需要在不影响全局表的情况下共享某些信息?

背景

在Lua中创建库需要库影响全局命名空间被认为是不好的形式:

--> somelib.lua <--
SomeLib = { ... }

--> usercode.lua <--
require 'somelib'
print(SomeLib) -- global key created == bad
Run Code Online (Sandbox Code Playgroud)

相反,最好的做法是创建一个使用局部变量的库,然后返回它们以供用户根据需要进行分配:

--> somelib.lua <--
local SomeLib = { ... }
return SomeLib

--> usercode.lua <--
local theLib = require 'somelib' -- consumers name lib as they wish == good
Run Code Online (Sandbox Code Playgroud)

使用单个文件时,上述模式工作正常.但是,当您有多个相互引用的文件时,这会变得相当困难.

具体例子

如何重写以下文件套件以使断言全部通过?理想情况下,重写将在磁盘上保留相同的文件,并为每个文件保留责任.(通过将所有代码合并到单个文件中进行重写是有效的,但没有帮助;)

--> test_usage.lua <--
require 'master'

assert(MASTER.Simple)
assert(MASTER.simple)
assert(MASTER.Shared)
assert(MASTER.Shared.go1)
assert(MASTER.Shared.go2)
assert(MASTER.Simple.ref1()==MASTER.Multi1)
assert(pcall(MASTER.Simple.ref2))
assert(_G.MASTER == nil)                   -- Does not currently pass 
Run Code Online (Sandbox Code Playgroud)

 

--> master.lua <--
MASTER = {}
require 'simple'
require 'multi'
require 'shared1'
require 'shared2'
require 'shared3'
require 'reference'

--> simple.lua <--
MASTER.Simple = {}
function MASTER:simple() end

--> multi.lua <--
MASTER.Multi1 = {}
MASTER.Multi2 = {}

--> shared1.lua <--
MASTER.Shared = {}

--> shared2.lua <--
function MASTER.Shared:go1() end

--> shared3.lua <--
function MASTER.Shared:go2() end

--> reference.lua <--
function MASTER.Simple:ref1() return MASTER.Multi1 end
function MASTER.Simple:ref2() MASTER:simple()      end
Run Code Online (Sandbox Code Playgroud)

失败:设置环境

我想通过自我引用将环境设置为主表来解决问题.require但是在调用函数时这不起作用,因为它们会改变环境:

--> master.lua <--
foo = "original"
local MASTER = setmetatable({foo="captured"},{__index=_G})
MASTER.MASTER = MASTER
setfenv(1,MASTER)
require 'simple'

--> simple.lua <--
print(foo)         --> "original"
MASTER.Simple = {} --> attempt to index global 'MASTER' (a nil value)
Run Code Online (Sandbox Code Playgroud)

小智 4

您赋予 master.lua 两项职责:

  1. 它定义了公共模块表
  2. 它导入所有子模块

相反,您应该为 (1) 创建一个单独的模块并将其导入所有子模块中:

--> common.lua <--
return {}

--> master.lua <--
require 'simple'
require 'multi'
require 'shared1'
require 'shared2'
require 'shared3'
require 'reference'
return require'common' -- return the common table

--> simple.lua <--
local MASTER = require'common' -- import the common table
MASTER.Simple = {}
function MASTER:simple() end
Run Code Online (Sandbox Code Playgroud)

ETC。

最后,更改第一行test_usage.lua以使用局部变量:

--> test_usage.lua <--
local MASTER = require'master'
...
Run Code Online (Sandbox Code Playgroud)

现在测试应该通过了。