lua的私有性(privacy)

很多人认为私有性是面向对象语言的应有的一部分。每个对象的状态应该是这个对象自己的事情。在一些面向对象的语言中,比如C++和Java你可以控制对象成员变量或者成员方法是否私有。其他一些语言比如Smalltalk中,所有的成员变量都是私有,所有的成员方法都是公有的。第一个面向对象语言Simula不提供任何保护成员机制。

  如前面我们所看到的Lua中的主要对象设计不提供私有性访问机制。部分原因因为这是我们使用通用数据结构tables来表示对象的结果。但是这也反映了后来的Lua的设计思想。Lua没有打算被用来进行大型的程序设计,相反,Lua目标定于小型到中型的程序设计,通常是作为大型系统的一部分。典型的,被一个或者很少几个程序员开发,甚至被非程序员使用。所以,Lua避免太冗余和太多的人为限制。如果你不想访问一个对象内的一些东西就不要访问(If you do not want to access something inside an object, just do not do it.)。

  然而,Lua的另一个目标是灵活性,提供程序员元机制(meta-mechanisms),通过他你可以实现很多不同的机制。虽然Lua中基本的面向对象设计并不提供私有性访问的机制,我们可以用不同的方式来实现他。虽然这种实现并不常用,但知道他也是有益的,不仅因为它展示了Lua的 一些有趣的角落,也因为它可能是某些问题的很好地解决方案。设计的基本思想是,每个对象用两个表来表示:一个描述状态;另一个描述操作(或者叫接口)。对 象本身通过第二个表来访问,也就是说,通过接口来访问对象。为了避免未授权的访问,表示状态的表中不涉及到操作;表示操作的表也不涉及到状态,取而代之的 是,状态被保存在方法的闭包内。例如,用这种设计表述我们的银行账号,我们使用下面的函数工厂创建新的对象:

复制代码

function newAccount (initialBalance)
    local self = { balance = initialBalance }
    local withdraw = function (v)
       self.balance = self.balance - v
    end

    local deposit = function (v)
       self.balance = self.balance + v
    end

    local getBalance = function () 
        return self.balance 
    end
    
    return {
       withdraw = withdraw,
       deposit = deposit,
       getBalance = getBalance
    }
end

acc1 = newAccount(100.00)
acc1.withdraw(40.00)

print(acc1.getBalance())   -- 60
acc1.deposit(40)
print(acc1.getBalance())   -- 100

print(acc1.balance)        -- nil

复制代码

  首先,函数创建一个表用来描述对象的内部状态,并保存在局部变量self内。然后,函数为对象的每一个方法创建闭包(也就是说,嵌套的函数实例)。最后,函数创建并返回外部对象,外部对象中将局部方法名指向最终要实现的方法。这儿的关键点在于:这些方法没有使用额外的参数self,代替的是直接访问self。因为没有这个额外的参数,我们不能使用冒号语法来访问这些对象。函数只能像其他函数一样调用:   acc1.deposit(40)     acc1.getBalance()

  这种设计实现了任何存储在self表中的部分都是私有的,newAccount返回之后,没有什么方法可以直接访问对象,我们只能通过newAccount中定义的函数来访问他。虽然我们的例子中仅仅将一个变量放到私有表中,但是我们可以将对象的任何的部分放到私有表中。我们也可以定义私有方法,他们看起来象公有的,但我们并不将其放到接口中。例如,我们的账号可以给某些用户取款享有额外的10%的存款上限,但是我们不想用户直接访问这种计算的详细信息,我们实现如下:

复制代码

 1 function newAccount (initialBalance)
 2     local self = { balance = initialBalance , 
 3                    LIM = 1314 }
 4 
 5     local withdraw = function (v)
 6        self.balance = self.balance - v
 7     end
 8 
 9     local extra = function ()
10        if self.balance > self.LIM then
11            return self.balance*0.10
12        else
13            return 0
14        end
15     end
16 
17     local deposit = function (v)
18        self.balance = self.balance + v
19     end
20 
21     local getBalance = function () 
22         return self.balance + extra()  --[此处非self.extra()]
23     end
24     
25     return {
26        withdraw = withdraw,
27        deposit = deposit,
28        getBalance = getBalance,
29        -- extra = extra, --因为这里没有暴露extra方法
30     }
31 end

复制代码

       这样,对于用户而言就没有办法直接访问extra函数了;如此 也就实现lua private function。

      使用table来实现面向对象的编程方式,几乎可以实现所有面向对象的编程特性

    但它没有也不想去实现的就是对象的私密性,也就是c++里的private、public、protected

    这与lua设计的初衷有关,lua定位于小型的程序开发,参与一个工程的人不会很多,自行约束

    非要实现私密性的话lua也不是不能,只是不能再使用table和元表的方式了

 

      它可以通过如上述所写方方式实现,在闭包里定义一个table的upvalue,然后把所有闭包函数都定义在这里table里,

    然后返回这个table,用key访问内部方法

      使用闭包实现对象的方式比用table效率高并实现了绝对的私密性,但无法实现继承,相当于简单的小对象

    甚至可以在闭包里仅定义一个方法,然后通过key来判断调用是什么方法

      Tcl/Tk对它的窗口部件就使用这种方法

Lua 中,可以使用闭包(closure)来模拟私有字段,从而实现面向对象编程中的封装。所谓私有字段,指的是只能在类内部访问的字段,外部无法直接访问或修改。下面是一个使用闭包实现私有字段的示例: ```lua -- 定义一个类 local MyClass = {} -- 创建一个闭包,用来保存私有变量 local PrivateVars = {} -- 定义一个公有方法,用来设置私有变量 function MyClass:setPrivateVar(name, value) PrivateVars[self][name] = value end -- 定义一个公有方法,用来获取私有变量 function MyClass:getPrivateVar(name) return PrivateVars[self][name] end -- 定义一个方法,用来输出私有变量 function MyClass:hello() print("Hello, my name is " .. self:getPrivateVar("name") .. "!") end -- 创建一个实例 local obj = {} PrivateVars[obj] = { name = "Jack" } setmetatable(obj, { __index = MyClass }) -- 调用方法 obj:hello() -- 输出 "Hello, my name is Jack!" ``` 在上面的示例中,我们定义了一个类 `MyClass`,并创建了一个闭包 `PrivateVars`,用来保存私有变量。闭包中的每个键值对都是一个实例的私有变量,通过使用 `PrivateVars[self]` 来获取当前实例的私有变量表。 然后,我们在类中定义了三个公有方法,分别用来设置、获取和输出私有变量。在方法中,我们通过 `PrivateVars[self]` 来获取当前实例的私有变量表,并使用 `name` 来访问私有变量。由于私有变量保存在闭包中,外部无法直接访问或修改,从而实现了封装。 最后,我们创建了一个实例 `obj`,并在 `PrivateVars[obj]` 中设置了 `name` 私有变量的初始值为 "Jack"。然后,我们调用了 `obj:hello()` 方法来输出私有变量的值。 需要注意的是,使用闭包来模拟私有字段会增加代码的复杂度,同时也会带来一定的能开销。因此,在设计程序时需要根据实际情况进行权衡和选择。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值