¿Es esto algún tipo de convención con experiencia que los programadores de Lua recomiendan o hay ventajas genuinas para hacerlo de esta manera?
No es típico. La ventaja es que el estado del objeto es privado, pero eso no es suficiente como para recomendarlo.
Veo este patrón repetidamente.
Nunca lo he visto antes, y solo ocurre una vez en la fuente que ha publicado.
EDITAR: Agregar una respuesta a una pregunta formulada en los comentarios debajo de esta publicación.
Una función que accede a las variables locales externas enlaza a esas variables y se denomina 'cierre'. Lua (por razones históricas) se refiere a esas variables vinculadas como 'valores ascendentes'. Por ejemplo:
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
y b
están obligados a cierres diferentes copias de i
, como se puede ver en la salida. En otras palabras, puede pensar en un cierre como función con su propio estado privado. Usted puede usar esto para simular objetos:
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
devuelve una tabla de cierres, cada uno con destino a los locales y x
y
. La tabla no contiene el estado del punto, los cierres sí lo hacen, a través de sus valores ascendentes. Un punto importante es que cada vez que se llama Point
crea nuevos cierres, lo que no es muy eficiente si tiene grandes cantidades de objetos.
Otra forma de crear clases en Lua es crear funciones que toman un cuadro como el primer argumento, con el estado de ser almacenados en la tabla:
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)
Hasta el momento, todavía estamos creando nuevas copias de cada método, pero ahora que no estamos confiando en upvalues para el estado, podemos arreglar eso:
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
ahora los métodos se crean una vez, y todos los casos Point
compartir los mismos cierres. Una mejor manera de hacer esto es utilizar las instalaciones metaprogramming de Lua para hacer nuevos Point
casos se ven automáticamente en PointClass
por métodos no se encuentran en la propia instancia:
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)
Esta es una forma más idiomática de la creación de clases en Lua. Es más eficiente en cuanto a la memoria y más flexible (en particular, facilita la implementación de la herencia).
¿Estás seguro de haberlo simplificado correctamente? ¿Podría vincular algunos de estos ejemplos en su forma completa? – Amber
Sí, el 'patrón' que has publicado parece completamente sin sentido. ¿Dice que ve ese "mucho", puede vincular a un ejemplo? – Mud
He editado la pregunta con un ejemplo más completo y he citado donde puede descargar el archivo fuente completo. Si Lua es como otros idiomas que conozco, parece que la segunda forma hace más con menos. –