lua中封装对象的代理表

问题描述 投票:0回答:1

练习21.4:双重表示的一种变体是使用代理来实现对象(称为“跟踪表访问”的部分)。每个对象都由一个空代理表表示。内部表将代理映射到携带对象状态的表。该内部表无法从外部访问,但方法使用它来将其自身参数转换为它们操作的真实表。使用这种方法实现帐户示例并讨论其优点和缺点。

local AccountProxy = {}

-- Internal table mapping proxies to actual Account tables
local accounts = {}

function AccountProxy:new()
 local proxy = {}
 local account = {balance = 0}
 accounts[proxy] = account

setmetatable(proxy, {   
    __newindex = function(_, k, v)
        account[k] = v
    end,
    __index = function(_, k)
        return account[k]
    end,
  })

 return proxy
end

function AccountProxy:withdraw(v)
   self.balance = (self.balance or 0) - v
end

function AccountProxy:deposit(v)
  self.balance = (self.balance or 0) + v 
end

function AccountProxy:getBalance()
   return self.balance or 0
end

-- Example usage
local acc_1 = AccountProxy:new()
acc_1:deposit(100.0)
print(acc_1:getBalance())  -- 100.0
acc_1:withdraw(50.0)
print(acc_1:getBalance())  -- 50.0`

我尝试调用零值(方法“存款”)。有什么建议吗?

我需要对Account对象和余额进行封装作为Account的属性

lua proxy encapsulation
1个回答
0
投票

acc_1:deposit(100.0)
acc_1.deposit(acc_1, 100.0)
的语法糖。您面临的直接问题是
acc_1.deposit
nil
。首先,
acc_1
是空的。这是设计使然——它是由
proxy
返回的
AccountProxy:new
。因为键不存在,所以它会检查元表。
__index
方法在相应的
account
表中查找,但这只有
balance
acc_1
AccountProxy.deposit
没有任何连接。你可以称之为
AccountProxy.deposit(acc_1, 100.0)
,但这并不是真正的传统的面向对象模式。

您希望

acc_1.deposit
与实际方法关联,因此您必须将
deposit
直接放入
acc_1
中,或者通过元表访问它。我认为这两个都很好,但我可能更喜欢第二个,因为您不想从实例方法访问
new
中定义的任何局部变量。例如

function AccountProxy:new()
  local proxy = {}
  -- ...
  function proxy:deposit()
    -- do the deposit ...
  end
  -- ...
  return proxy
end

function AccountProxy:new()
  local proxy = {}
  setmetatable(proxy, {
    __index = AccountProxy,
  })
  -- ...
  return proxy
end

为此目的使用元表可能会干扰您现有的元表,但我认为您现有的元表正在破坏您的目的。据说这里代理的目的是封装——防止外部代码访问

balance
。您提供的元表只允许任何代码(内部或外部)访问余额。 (是的,这是引用示例中使用的元表,但据我所知,这只是强制访问通过特定路径的示例,而不是专门使该路径使用元表。)

如果没有元表,您需要另一种方式从

balance
检索
proxy
;并且您已经按照练习的建议部分实施了这一点。给定
proxy
,您可以查找
accounts[proxy]
,例如

function AccountProxy:getBalance()
   return accounts[self].balance or 0
end

假设外部代码无法访问

accounts
(可以通过词法作用域强制执行),那么它就无法直接与
balance
交互,除非通过提供的方法。

(使用

accounts
并不是进行此类映射或获得此类封装的唯一方法。我倾向于使用闭包,但我不确定这是否是其他地方讨论的模式。)

其他风格注释:

  • AccountProxy:new
    不使用
    self
    ;概念上不是实例方法的东西可能更适合仅使用
    .
    而不是
    :
  • 您的方法都处理余额可能为
    nil
    的情况。我不一定对此有问题,但如果我知道平衡永远不会
    nil
    (我认为在这里是正确的),我宁愿不这样做。
© www.soinside.com 2019 - 2024. All rights reserved.