Lua封装私有变量和私有方法

使用Lua的table模拟面向对象有一个问题就是没法设置私有变量和私有方法,这样在团队开发的时候很可能造成一些不能修改的值被修改的问题。最好的解决办法是通过代码管理、新人培训、CodeReview来解决这个问题而不是通过Lua的语法来封装成私有。但是要做还是可以做到的。

第一种,整个类是直接使用的单例,把私有变量和方法设置为local就行。

local MyClass = {
    publicValue = "public"
}

local privateValue = 0

local function PrivateFunc()
    print("This is a private function")
    privateValue = privateValue + 1
    print("call private function "..privateValue.." times")
end

function MyClass:PublicFunc()
    print("This is a public function")
    print("you can call privateFunc from here")
    PrivateFunc()
end

return MyClass

调用:

local MyClass = require "MyClass"
MyClass:PublicFunc()
print(MyClass.publicValue)
MyClass:PrivateFunc()       --无效
print(MyClass.privateValue) --无效

这一种是有一定使用价值的,没有理解难度,对性能也没有造成任何影响。事实上这就是Lua5.1以后引入的最重要的模块机制。缺点就是一定只能单例使用,不能把MyClass看做类实例化出多个对象使用。

第二种,将MyClass看做类,使用的是“实例化”的对象。

local MyClass = {
}
--存放各个对象的私有变量
local private = {}
setmetatable(private, {__mode = "k"})   --必须让private变成weak表

function MyClass:New(name)
    local privateTb = {
        privateValue = name,
        PublicPrint = MyClass.PublicPrint,
        CallPrivatePrint = MyClass.CallPrivatePrint,
        --这里放成员方法和私有变量
    }
    local newObject = setmetatable(
        {
            publicValue = "public",
            --这里放公有变量
        }, 
    {
    __index = function(tb,key)
        return privateTb[key]
    end,
    __newindex = function(tb, key, val)
        if privateTb[key] ~= nil then
           print("can not modify private value")
           return
        end
        rawset(tb,key,val)
    end})
    private[newObject] = privateTb
    return newObject
end
--Public
function MyClass:PublicPrint()
    print("This is a public function")
end
--Private
local function PrivatePrint(self)
    print("This is a private function")
    print("private value is "..private[self].privateValue)
end
--Public
function MyClass:CallPrivatePrint()
    PrivatePrint(self)
end

return MyClass

调用:

local MyClass = require "MyClass"
local obj = MyClass:New("aaa")
obj.publicValue = "aaa public"
print(obj.publicValue)
obj.privateValue = "aaa private"    --无效,修改被阻止
print(obj.privateValue)             --还是原来的值
obj:PublicPrint()
obj:CallPrivatePrint()

local obj2 = MyClass:New("bbb")
obj2:CallPrivatePrint()
obj2:PrivatePrint()

第二种方法在第一种的基础上新增了New方法,因为各个新对象必须有自己独一份的私有变量,所以用privateTb存储,并在local变量private中进行保存。这里需要注意的点是private必须是key弱表。对于弱表很多人可能不太理解,简单来说就是设置了__mode元表的表(有三个值“k”、"v"、“kv”,分别代表key弱引用,value弱引用,key和value均为弱引用),其引用不会被垃圾管理器计数,如果只有弱表有引用那么依然会被垃圾回收。很显然如果private不为弱表的话,由于key是所有的对象,那么意味着这些对象都无法被回收。

第二种方法实现了私有变量不被修改,私有方法无法访问。实际上__index可以直接使用privateTb,但是为了外部无论如何都无法修改私有变量,改用了函数进行返回。这样即使getmetatable也无法修改私有变量。但是为了不让新的同名变量覆盖私有变量,必须设置__newIndex,并且判断该变量是否在privateTb中,由于设置新的变量都会访问__newIndex,无疑带来了额外的性能消耗。而访问私有变量也必须通过private表,对代码理解上也增加了成本。因此若无必要不需要封装到这个地步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值