tl;dr:什么设计模式允许您将 Lua 代码拆分到需要共享某些信息而不影响全局表的多个文件上?
背景
在 Lua 中创建库会影响全局命名空间,这种做法被认为是不好的形式:
--> somelib.lua <--
SomeLib = { ... }
--> usercode.lua <--
require 'somelib'
print(SomeLib) -- global key created == bad
相反,最好的做法是创建一个使用局部变量的库,然后将它们返回给用户以在他们认为合适的时候进行分配:
--> somelib.lua <--
local SomeLib = { ... }
return SomeLib
--> usercode.lua <--
local theLib = require 'somelib' -- consumers name lib as they wish == good
使用单个文件时,上述模式可以正常工作。但是,当您有多个相互引用的文件时,这变得相当困难。
具体例子
您如何重写以下文件套件以使断言全部通过?理想情况下,重写将在磁盘上保留相同的文件以及每个文件的职责。(通过将所有代码合并到一个文件中进行重写是有效的,但没有帮助;)
--> 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
--> 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
失败:设置环境
我想通过使用自我参考将环境设置到我的主表来解决问题。然而,这在调用函数时不起作用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)