OpenResty普通模块与实例化模块

在OpenResty里面做Lua开发,由于大多数的应用程序开发,使用的都是面向对象的编程方式。而Lua是一门脚本语言,如果不依赖OpenResty这样的中间件,一般不适合独立开发应用程序。

那么在OpenResty里用Lua开发如何模拟出面向对象风格的开发模式呢?下面做一下介绍:

普通模块

Lua中的普通模块可以类比为java中的单实例,意思就是在整个应用中不需要创建多个实例对象,仅用一个实例就可以满足应用需要。这里就如Java Web开发中的Service。

但是这里只能类比,因为在如下代码块③中,如果直接增加了可以执行的代码,当该模块被其他模块require时,每一个require都会执行③部分的代码块。而在java语言中,代码的执行逻辑必须在方法体中,因此不存在这部分的代码。

还有一点需要注意的是,如果java中使用了Spring MVC来作为容器开发,Service在全局范围内就是一个对象(同一个内存地址)。而在Lua中,每一次require都相当于会生成一个“对象”(对于不同模块的require来说)。 代码如下:

local cjson = require "cjson"
local mysql = require("libs.mysql")

local _M = { _VERSION = "1.0" }

-- 查询列表 ①
function _M:list()
	local db = mysql:new()
	local sql = "select * from article_type"
    -- 可以使用self变量

	db:query("SET NAMES utf8")
	local res, err, errno, sqlstate = db:query(sql)
	db:close()
	if not res then
		ngx.say(err)
		return {}
	end

	return res
end

-- 查询详情 ②
function _M.detail( self, typeId )
	local db = mysql:new()
	local sql = "select * from article_type where type_id = %d"
	sql = string.format(sql, tonumber(typeId))

	db:query("SET NAMES utf8")
	local res, err, errno, sqlstate = db:query(sql)
	db:close()
	if not res then
		ngx.say(err)
		return {}
	end

	return res[1]
end

-- 执行代码 ③

return _M

如上代码所示,在普通模块中定义方法可以有① ②两种模式:_M:list()和_M.detail(self)。这两种定义的方式是等效的,在方法内部都可以使用self变量(如:在list方法中调用self:type()),类似于java中的this关键字。

实例化模块

通过上面普通模块的介绍,我们可以看到对于_M开头定义的方法都是共享的。如果此时我们需要在模块中new一个table(类似于java中的new Object()),这样就需要方法每次返回新的内容了。

local _M = { _VERSION = "1.0" }

function _M:error( msg )
	return _M:new(false, "9999", msg, nil)
end

function _M:success( msg, data )
	return _M:new(true, "1000", msg, data)
end

function _M:new(success, code, msg, data)
    local temp = {}
    temp.success = success
    temp.code = code
    temp.msg = msg
    temp.data = data
    return setmetatable(temp, _M)
end

return _M

通过上面的定义,可以实现new的操作。对于需要共享的变量放在_M当中,每个new操作的不同变量放在temp当中。最后通过元表的方式返回,当访问变量时,会先从temp中查找,然后再去_M当中查找。