第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于后续版本,但有一些差异。
第四版针对 Lua 5.3,可在 亚马逊 和其他书店购买。
购买本书,您还可以帮助支持 Lua 项目


16.4 – 隐私

许多人认为隐私是面向对象语言不可或缺的一部分;每个对象的状态应该是其自己的内部事务。在某些面向对象语言(如 C++ 和 Java)中,您可以控制对象字段(也称为实例变量)或方法是否在对象外部可见。其他语言(如 Smalltalk)使所有变量为私有,所有方法为公有。第一种面向对象语言 Simula 没有提供任何类型的保护。

我们之前展示过的 Lua 中对象的主要设计不提供隐私机制。部分原因是,我们使用通用结构(表)来表示对象。但这还反映了 Lua 背后的一些基本设计决策。Lua 并非用于构建大型程序,在大型程序中,许多程序员会参与很长时间。恰恰相反,Lua 针对中小型程序,通常是更大系统的一部分,通常由一个或几个程序员甚至非程序员开发。因此,Lua 避免了过多的冗余和人为限制。如果您不想访问对象内部的某个内容,只需不访问即可。

然而,Lua 的另一个目标是灵活,为程序员提供元机制,通过该机制她可以模拟许多不同的机制。尽管 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
首先,该函数创建一个表来保存内部对象状态,并将其存储在局部变量self中。然后,该函数为对象的每个方法创建一个闭包(即嵌套函数的实例)。最后,该函数创建并返回外部对象,该对象将方法名称映射到实际的方法实现。这里关键的一点是,这些方法不会将self作为额外的参数获取;相反,它们直接访问self。因为没有额外的参数,所以我们不使用冒号语法来操作此类对象。方法的调用就像任何其他函数一样
    acc1 = newAccount(100.00)
    acc1.withdraw(40.00)
    print(acc1.getBalance())     --> 60

这种设计为存储在self表中的任何内容提供了完全的隐私。在newAccount返回后,无法直接访问该表。我们只能通过在newAccount中创建的函数访问它。虽然我们的示例只将一个实例变量放入私有表中,但我们可以将对象的私有部分存储在该表中。我们还可以定义私有方法:它们就像公有方法,但我们不会将它们放入接口中。例如,我们的账户可能会为余额超过一定限额的用户提供 10% 的额外信用,但我们不希望用户访问此计算的详细信息。我们可以按如下方式实现此功能

    function newAccount (initialBalance)
      local self = {
        balance = initialBalance,
        LIM = 10000.00,
      }
    
      local extra = function ()
        if self.balance > self.LIM then
          return self.balance*0.10
        else
          return 0
        end
      end
    
      local getBalance = function ()
        return self.balance + self.extra()
      end
    
      ...
同样,任何用户都无法直接访问extra函数。