lua中的面向对象

lua实现面向对象编程是基于元表 metatable,元方法 __index 来实现的。

lua中的语法糖

lua的table中可以定义函数,如下图:

local a = {
    x = 1,
    print = function(obj)
        print("x=" .. obj.x)
    end
}

如上图,定义了一个名叫 a 的table,可以通过调用 a.print(a) 来打印table a 中的 x key对应的value值。

像是 a.print(a) 这种形式,在lua中可以简化为 a:print(),这是lua中的语法糖,使用 : 而不是 .,lua会默认把 “调用者” 给传递进去。

元表 metatable

元表的表现行为类似于操作符重载,比如我们可以重载 __add,来计算两个 Lua 数组的并集;或者重载 __tostring,来定义转换为字符串的函数。

Lua 提供了两个处理元表的函数:

  • setmetatable(table, metatable), 用于为一个 table 设置元表
  • getmetatable(table),用于获取 table 的元表

比如:

local version = { major = 1, minor = 1, patch = 1 }
version = setmetatable(version, 
    { 
        __tostring = function(t) 
            return string.format("%d.%d.%d", t.major, t.minor, t.patch) 
        end 
    }) 
print(tostring(version))        -- 1.1.1

这段代码的作用就是重载了 version 表的 tostring() 方法,如果不重载的话,直接调用默认方法,那么打印结果是:

local version = { major = 1, minor = 1, patch = 1 }
print(tostring(version))        -- table: 0x000647a8

元方法: __index

我们在 table 中查找一个元素时,首先会直接从 table 中查询,如果没有找到,就继续到元表的 __index 中查询。

local version = { major = 1, minor = 1}
version = setmetatable(version, 
    { 
        __index = function(t, key) 
            if key == "patch" then 
                return 2 
            end 
        end,
        __tostring = function(t) 
            return string.format("%d.%d.%d", t.major, t.minor, t.patch) 
        end 
    }) 
print(tostring(version))        -- 1.1.2

这样当 t.patch 取不到值时,就会去 __index 中进行查找。

__index 不仅可以是一个函数,也可以是一个table。

local version = { major = 1, minor = 1}
version = setmetatable(version, 
    { 
        __index = { patch=3 },
        __tostring = function(t) 
            return string.format("%d.%d.%d", t.major, t.minor, t.patch) 
        end 
    }
) 
print(tostring(version))        -- 1.1.3

上面的代码可以变换为如下形式:

local version = { major = 1, minor = 1}
local mt = {
    __index = { 
        patch=3 
    },
    __tostring = function(t) 
        return string.format("%d.%d.%d", t.major, t.minor, t.patch) 
    end
}
version = setmetatable(version, mt) 
print(tostring(version))        -- 1.1.3

元方法:__call

使用 __call 可以让 table 被调用。

local version = { major = 1, minor = 1, patch = 1 }
local function print_version(t)             
    print(string.format("%d.%d.%d", t.major, t.minor, t.patch))
end
version = setmetatable(version, 
    {
        __call = print_version
    }
) 

version()       -- 1.1.1

面向对象

lua-resty-mysql 是OpenResty 官方的 MySQL 客户端,里面就使用元表模拟了类和类方法,它的使用方式如下所示:

local mysql = require "resty.mysql"     -- 先引用 lua-resty 库
local db, err = mysql:new()     -- 新建一个类的实例
db:set_timeout(1000)    -- 调用类的方法

那么lua-resty-mysql的源码是如何实现的呢?

local _M = { _VERSION = '0.21' } -- 使用 table 模拟类
local mt = { __index = _M } -- mt 即 metatable 的缩写,__index 指向类自身

-- 类的构造函数
function _M.new(self) 
    local sock, err = tcp() 
    if not sock then 
        return nil, err 
    end 
    return setmetatable({ sock = sock }, mt) -- 使用 table 和 metatable 模拟类的实例
end 

-- 类的成员函数 
function _M.set_timeout(self, timeout) 
    -- 使用 self 参数,获取要操作的类的实例 
    local sock = self.sock 
    if not sock then 
        return nil, "not initialized" 
    end 
    return sock:settimeout(timeout)
end

上面的代码中,-M 这个table代表的就是mysql类,_M.new(self) 方法中返回一个 setmetatable({ sock = sock }, mt) 就是返回一个类的对象,socket 这个属性就是这个对象的成员属性,而不是类的属性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值