通过模块实现类 |
|
请参阅本页底部有关如何使用的示例。如果您愿意,可以随意将功劳归于 James Rhodes ([email protected]),但没有必要这样做。
-- Overwrite our loading functionality. package.loaders = {} package.loaders[1] = function(module, env) -- Declare our variables local so they don't -- interfere with the main program. if (env == nil) then env = _G end local path, ext, found_path, e, fhandle local chunk, lmodule, _T, lfmodulepath local indent, l, n, ldotpos, classname local namespace, class_funcs, class, mt local __init, f, v, k, tdotpos, __base, i local class_inheritance = {} local inherit_classname, inherit_namespaces local inheritance_sandbox, i2 local function func_inherit(class) if (lmodule._T == nil) then lmodule._T = {} end table.insert(class_inheritance, class) end local function func_name(name) local ldotpos, tdotpos, classname, namespaces local z, x, c ldotpos = string.len(name) tdotpos = string.len(name) classname = nil namespaces = {} while (ldotpos > 0) do if (string.sub(name,ldotpos,ldotpos) == ".") then if (classname == nil) then classname = string.sub(name, ldotpos + 1, tdotpos) else table.insert(namespaces,1,string.sub(name, ldotpos + 1, tdotpos)) end tdotpos = ldotpos - 1 end ldotpos = ldotpos - 1 end if (tdotpos > 0) then table.insert(namespaces,1,string.sub(name, ldotpos + 1, tdotpos)) end z = {} for c, x in ipairs(namespaces) do table.insert(z,1,x) end namespaces = z if (lmodule._T == nil) then lmodule._T = {} end lmodule._T["NAME"] = classname if (#namespaces > 0) then lmodule._T["NAMESPACE"] = namespaces else lmodule._T["NAMESPACE"] = nil end end local function func_description(desc) if (lmodule._T == nil) then lmodule._T = {} end lmodule._T["DESCRIPTION"] = desc end local function func_author(author) if (lmodule._T == nil) then lmodule._T = {} end lmodule._T["AUTHOR"] = author end local function func_lastmodified(date) if (lmodule._T == nil) then lmodule._T = {} end lmodule._T["LASTMODIFIED"] = date end -- Replace the "." in the requested module with -- backslashes. path = string.gsub(module, "%.", "/") path = "./" .. path ext = {"rcs", "rs", "rks", "lua"} found_path = nil for k, e in pairs(ext) do fhandle = io.open(path .. "." .. e, "r") if (fhandle ~= nil) then fhandle:close() found_path = path .. "." .. e break end end if (found_path == nil) then print("ERR : Unable to locate module at " .. path .. ".{rcs,rs,rks,lua}.") return nil, "Unable to locate module at " .. path .. ".{rcs,rs,rks,lua}." end -- Since the file exists, we're now going to load it. chunk = loadfile(found_path) if (chunk == nil) then print("ERR : Module " .. module .. " contains syntax errors and cannot be included in the program.") return nil, "Module " .. module .. " contains syntax errors and cannot be included in the program." end -- Isolate the class name from the namespace component ldotpos = string.len(module) while (ldotpos > 0) do if (string.sub(module,ldotpos,ldotpos) == ".") then break end ldotpos = ldotpos - 1 end classname = string.sub(module, ldotpos + 1) if (ldotpos ~= 0) then namespace = string.sub(module, 1, ldotpos - 1) else namespace = "" end -- Run the chunk() function inside a sandbox, so we can inspect the module. lmodule = {} lmodule[classname] = {} lmodule["inherits"] = func_inherit lmodule["name"] = func_name lmodule["description"] = func_description lmodule["author"] = func_author lmodule["lastmodified"] = func_lastmodified setfenv(chunk, lmodule) chunk() -- Check to see if _T exists, if it doesn't, show that the module can't be loaded. if (lmodule["_T"] == nil) then print("ERR : Module " .. module .. " does not specify module information.") return nil, "Module " .. module .. " does not specify module information." end -- Move the _T table from the code block, into a local variable. _T = lmodule["_T"] lmodule["_T"] = nil -- Sanitize the _T variable we will use. _T["NAME"] = tostring(_T["NAME"]) if (_T["DESCRIPTION"] ~= nil) then _T["DESCRIPTION"] = tostring(_T["DESCRIPTION"]) end if (_T["AUTHOR"] ~= nil) then _T["AUTHOR"] = tostring(_T["AUTHOR"]) end if (_T["LASTMODIFIED"] ~= nil) then _T["LASTMODIFIED"] = tostring(_T["LASTMODIFIED"]) end -- Verify that the module is located at the correct location (NAMESPACE "." NAME == module) if (_T["NAMESPACE"] ~= nil) then lfmodulepath = table.concat(_T["NAMESPACE"], ".") .. "." .. _T["NAME"] if (lfmodulepath ~= module) then print("ERR : Module name mismatch. Loaded from " .. module .. ", but code specifies " .. lfmodulepath .. ".") return nil, "Module name mismatch. Loaded from " .. module .. ", but code specifies " .. lfmodulepath .. "." end else lfmodulepath = _T["NAME"] end -- Now create the namespace tables if required. l = env if (_T["NAMESPACE"] ~= nil) then for k, n in pairs(_T["NAMESPACE"]) do l[n] = {} l = l[n] end end -- Get the class functions. class_funcs = lmodule[classname] lmodule[classname] = nil __init = nil __base = nil -- Build up the class. class = {} if (#class_inheritance > 0) then -- We need to evaluate all of the inherited class -- in order, to build up. for k, v in pairs(class_inheritance) do -- Isolate the class name from the namespace component print("INFO: Loading " .. v .. " in required module. Namespaces / classes should not leak.") ldotpos = string.len(v) tdotpos = string.len(v) inherit_classname = nil inherit_namespaces = {} while (ldotpos > 0) do if (string.sub(v,ldotpos,ldotpos) == ".") then if (inherit_classname == nil) then inherit_classname = string.sub(v, ldotpos + 1, tdotpos) else table.insert(inherit_namespaces,1,string.sub(v, ldotpos + 1, tdotpos)) end tdotpos = ldotpos - 1 end ldotpos = ldotpos - 1 end if (tdotpos > 0) then table.insert(inherit_namespaces,1,string.sub(v, ldotpos + 1, tdotpos)) end inheritance_sandbox = {} package.loaders[1](v, inheritance_sandbox) -- Load the class into i. i = inheritance_sandbox for i2, n in pairs(inherit_namespaces) do i = i[n] end i = i[inherit_classname] -- Now copy all of the inherited classes functions. for n, f in pairs(i) do if (n == "__init") then __base = f setfenv(__base, env) elseif (type(f) == "function") then class[n] = f setfenv(class[n], env) elseif (type(f) ~= "function") then class[n] = f end end end end for n, f in pairs(class_funcs) do if (n == "__init") then __init = f setfenv(__init, env) else class[n] = f setfenv(class[n], env) end end for n, v in pairs(lmodule) do if (n ~= "inherits" and n ~= "name" and n ~= "description" and n ~= "author" and n ~= "lastmodified") then if (type(v) == "function") then print("WARN: " .. lfmodulepath .. ":0: Function " .. n .. " defined without class context. It is not included in the class definition.") else -- Make the specified variable a static variable. class[n] = v end end end mt = {} mt.__call = function(class_tbl, ...) local obj = {} setmetatable(obj, class) if (__init ~= nil) then __init(obj, ...) elseif (__base ~= nil) then __base(obj, ...) end return obj end class.__index = class class.__init = __init class.__base = __base setmetatable(class, mt) -- Now put the newly generated class in the namespace. l[classname] = class -- Show module loaded message print("INFO: " .. lfmodulepath .. ":0: Loaded module " .. _T["NAME"] .. ".") indent = string.rep(" ", string.len("INFO: " .. lfmodulepath .. ":0: ")) if (_T["NAMESPACE"] ~= nil) then print(indent .. " in namespace " .. namespace) end if (_T["DESCRIPTION"] ~= nil) then print(indent .. " Description: " .. _T["DESCRIPTION"]) end if (_T["AUTHOR"] ~= nil) then print(indent .. " Author: " .. _T["AUTHOR"]) end if (_T["LASTMODIFIED"] ~= nil) then print(indent .. " Last Modified: " .. _T["LASTMODIFIED"]) end return function() end end
重要的是要注意,要继承多个类,您只需多次调用 inherits()。最后一次调用将产生最大的影响,例如,如果两个由 inherits() 引用的类定义了 A() 函数,那么最后一次调用将是 A() 的定义,除非下面的 ClassB 自己覆盖它。即使您可以继承多个类,self.__base() 始终指向最后一个继承类的构造函数。
name "AnotherNamespace.ClassB" inherits "MainNamespace.ClassA" description [[Provides advanced class functionality.]] author "James Rhodes" lastmodified "20th May, 2010" myStaticVariable = 5 function ClassB:__init() self.__base() print "ClassB constructor!" end function ClassB:Function() print "Called from MyClassFunction (ClassB)" end