Это какое-то соглашение, которое рекомендуют опытные программисты Lua, или есть реальные преимущества в этом?
Это не типично. Преимущество в том, что состояние объекта является частным, но этого недостаточно, чтобы рекомендовать его.
Я вижу эту модель неоднократно.
Я никогда не видел этого раньше, и это происходит только один раз в источнике, который вы опубликовали.
РЕДАКТИРОВАТЬ: Добавление ответа на вопрос, заданный в комментариях ниже этого сообщения.
Функция, которая обращается к внешним локальным переменным , связывает с этими переменными и называется «замыканием». Lua (по историческим причинам) называет эти связанные переменные «повышенными значениями». Например:
local function counter()
local i = 1
return function()
print(i)
i = i + 1
end
end
local a, b = counter(), counter()
a() a() a() b() --> 1 2 3 1
a
и b
- это замыкания, связанные с различными копиями i
, как вы можете видеть из вывода. Другими словами, вы можете думать о замыкании как о функции с его собственным частным состоянием. Вы можете использовать это для симуляции объектов:
function Point(x,y)
local p = {}
function p.getX() -- syntax sugar for p.getX = function()
return x
end
function p.setX(x_)
x = x_
end
-- for brevity, not implementing a setter/getter for y
return p
end
p1 = Point(10,20)
p1.setX(50)
print(p1.getX())
Point
возвращает таблицу замыканий, каждое из которых привязано к местным жителям x
и y
. Таблица не содержит состояния точки, самих замыканий, через их значения вверх. Важным моментом является то, что каждый раз, когда вызывается Point
, он создает новых замыканий, что не очень эффективно, если у вас большое количество объектов.
Другим способом создания классов в Lua является создание функций, которые принимают таблицу в качестве первого аргумента с сохранением состояния в таблице:
function Point(x,y)
local p = {x=x,y=y}
function p:getX() -- syntax sugar for p.getX = function(self)
return self.x
end
function p:setX(x)
self.x = x
end
return p
end
p1 = Point(10,20)
p1:setX(50) -- syntax sugar for p1.setX(p1, 50)
print(p1:getX()) -- syntax sugar for p1.getX(p1)
Пока что мы все еще создаем новые копии каждого метода, но теперь, когда мы не полагаемся на повышенные значения для состояния, мы можем исправить это:
PointClass = {}
function PointClass:getX() return self.x end
function PointClass:setX(x) self.x = x end
function Point(x,y)
return {
x = x,
y = y,
getX = PointClass.getX,
setX = PointClass.getY,
}
end
Теперь методы создаются один раз, и все Point
экземпляров имеют одинаковые замыкания. Еще лучший способ сделать это - использовать средство метапрограммирования Lua, чтобы новые экземпляры Point
автоматически искали в PointClass
методы, не найденные в самом экземпляре:
PointClass = {}
PointClass.__index = PointClass -- metamethod
function PointClass:getX() return self.x end
function PointClass:setX(x) self.x = x end
function Point(x,y)
return setmetatable({x=x,y=y}, PointClass)
end
p1 = Point(10,20)
-- the p1 table does not itself contain a setX member, but p1 has a metatable, so
-- when an indexing operation fails, Lua will look in the metatable for an __index
-- metamethod. If that metamethod is a table, Lua will look for getX in that table,
-- resolving p1.setX to PointClass.setX.
p1:setX(50)
Это более идиоматический способ создания классов в Lua. Он более эффективен в памяти и более гибок (в частности, упрощает реализацию наследования).