创建相互依赖的Lua文件套件,而不会影响全局名称空间 [英] Create suite of interdependent Lua files without affecting the global namespace

查看:148
本文介绍了创建相互依赖的Lua文件套件,而不会影响全局名称空间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

tl; dr :哪种设计模式允许您将Lua代码分割为多个文件,这些文件需要共享一些信息而不影响全局表?

tl;dr: What design pattern allows you to split Lua code over multiple files that need to share some information without affecting the global table?

在Lua中创建库被认为是不好的形式,其中要求该库影响全局名称空间:

It is considered bad form to create a library in Lua where requiring the library affects the global namespace:

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

--> usercode.lua <--
require 'somelib'
print(SomeLib) -- global key created == bad

相反,最好的做法是创建一个使用局部变量的库,然后将其返回给用户,以供用户根据自己的意愿分配:

Instead, it is considered a best practice to create a library that uses local variables and then returns them for the user to assign as they see fit:

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

--> usercode.lua <--
local theLib = require 'somelib' -- consumers name lib as they wish == good

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

The above pattern works fine when using a single file. However, this becomes considerably harder when you have multiple files that reference each other.

如何重写以下文件套件,以便所有断言都可以通过?理想情况下,重写将在磁盘上保留相同的文件,并对每个文件负责. (通过将所有代码合并到一个文件中进行重写是有效的,但无济于事;)

How can you rewrite the following suite of files so that the assertions all pass? Ideally the rewrites will leave the same files on disk and responsibilities for each file. (Rewriting by merging all code into a single file is effective, but not helpful ;)

--> 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之类的函数时,这将不起作用,因为它们会改变环境:


Failure: Setting the Environment

I thought to solve the problem by setting the environment to my master table with a self-reference. This does not work when calling functions like require however, as they change the environment back:

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

推荐答案

您要赋予master.lua两项职责:

You are giving master.lua two responsibilities:

  1. 它定义了通用模块表
  2. 它导入所有子模块

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

Instead you should create a separate module for (1) and import it in all of the submodules:

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

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

Finally, change the first line of test_usage.lua to use a local variable:

--> test_usage.lua <--
local MASTER = require'master'
...

现在应该可以通过测试了.

The tests should now pass.

这篇关于创建相互依赖的Lua文件套件,而不会影响全局名称空间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆