2011-01-25 9 views
20

Lua no tiene soporte para OO, pero le permite construirlo usted mismo. ¿Podría compartir algunas de las formas en que uno puede implementar OO?¿Cómo se puede implementar OO en Lua?

Por favor, escriba un ejemplo por respuesta. Si tiene más ejemplos, publique otra respuesta.

+1

voy a añadir recompensa a ella, una vez que encontrar la manera, la respuesta tan superior en, por ejemplo nos dejó 'acerca a la semana' lo consigue:) –

+1

¿Quieres decir como C++? Bueno, mejor no entrar en ese debate :( –

+0

El PIL de Lua tiene referencias detalladas para él http://www.lua.org/pil/16.html –

Respuesta

35

Me gusta pensar en OOP como la encapsulación de datos dentro de un contenedor (el objeto) junto con un subconjunto de operaciones que se pueden realizar con estos datos. HAY mucho más, pero supongamos que esta simple definición es todo y construya algo en Lua a partir de ella (también cierta familiaridad con otras implementaciones de OO puede ser un buen impulso para el lector).

Como cualquier persona con un poco de exposición a Lua puede saber, tablas son una forma elegante para almacenar pares clave-valor y en combinación con cadenas, las cosas empiezan a ser muy interesante:

valores de cadena
local obj = {} -- a new table 
obj["name"] = "John" 
obj["age"] = 20 
-- but there's a shortcut! 
print("A person: " .. obj.name .. " of the age " .. obj.age) 

como se puede acceder a las claves de una tabla de forma muy parecida a los miembros de una estructura en C o a los miembros públicos de un objeto en C++/Java e idiomas similares.

Y ahora para un fantástico truco de magia: combinemos esto con funciones anónimas.

-- assume the obj from last example 
obj.hello = function() 
    print("Hello!") 
end 

obj.goodbye = function() 
    print("I must be going.") 
end 

obj.hello() 
obj.goodbye() 

Awesome right? Ahora tenemos medios para tener funciones almacenadas dentro de nuestras tablas, y de nuevo se puede ver que se asemeja a cómo se usan los métodos en otros lenguajes de OOP. Pero algo falta. ¿Cómo podemos acceder a los datos que pertenecen a nuestro objeto dentro de nuestras definiciones de métodos? Esto se trata en general por el cambio de la firma de las funciones en la tabla a algo como esto:

-- assume the obj from last example 
obj.inspect = function (self) 
    print("A person: " .. self.name .. " of the age " .. self.age) 
end 

obj.hello = function (self) 
    print(self.name .. ": Hello! I'm " .. self.name) 
end 

obj.goodbye = function (self) 
    print(self.name .. ": I must be going.") 
end 

-- now it receives the calling object as the first parameter 
obj.inspect(obj) -- A person: John of age 20 
obj.hello(obj) -- John: Hello! I'm John 
obj.goodbye(obj) -- John: I must be going 

Eso lo resuelve de una manera sencilla. Tal vez dibujar un paralelo con la forma en que funcionan las cosas en Python (los métodos siempre tienen un yo explícito) puede ayudarte a aprender cómo funciona esto en Lua. Pero chico, ¿no es inconveniente pasar todos estos objetos explícitamente en nuestras llamadas a métodos? Sí me molesta demasiado, así que no hay otro acceso directo para ayudarle en el uso de la programación orientada a objetos:

obj:hello() -- is the same as obj.hello(obj) 

Por último, sólo hemos arañado la superficie de cómo se puede hacer esto. Como se ha observado en Kevin Vermeer's comment, el Lua Users Wiki es una excelente fuente de información sobre este tema y allí puede aprender todo sobre cómo implementar otros aspectos importantes de OOP que se han descuidado en esta respuesta (miembros privados, cómo construir objetos, herencia , ...). Tenga en cuenta que esta forma de hacer las cosas es una pequeña parte de la filosofía de Lua, que le proporciona herramientas ortogonales simples capaces de construir construcciones más avanzadas.

+4

Buena respuesta. Añadiría que puede escribir 'function obj: hello() ** cosas aquí ** end' en lugar de' obj.hello = function (self) ** cosas aquí ** end', que se ve aún más agradable! – fouronnes

+0

'Me gusta pensar en OOP como el encapsulamiento de datos dentro de un contenedor (el Object) junto con un subconjunto de operaciones que se pueden realizar con estos datos' Esto se suele denominar basado en objetos, ya que no está completo OOP. A veces también se llama ADT (tipos de datos abstractos). ADT es más fácil de manejar y se puede implementar en casi cualquier idioma y en cualquier otro que no tenga herencia, tiene todo lo que uno necesita. – Nasser

9

Para una implementación oo rápido y sucio que hacer algo como -

function newRGB(r,g,b) 
    return { 
    red=r; 
    green=g; 
    blue=b; 
    name=''; 
    setName = function(self,name) 
     self.name=name; 
    end; 
    getName = function(self) 
     return self.name; 
    end; 
    tostring = function(self) 
     return self.name..' = {'..self.red..','..self.green..','..self.blue..'}' 
    end 
    } 
end 

que luego se puede utilizar como -

blue = newRGB(0,0,255); 
blue:setName('blue'); 

yellow = newRGB(255,255,0); 
yellow:setName('yellow'); 

print(yellow:tostring()); 
print(blue:tostring()); 

de un enfoque destacados más completa me gustaría utilizar una biblioteca oo como fue mencionado por eemrevnivek. También puede encontrar una función de clase simple here que está entre lleno en la biblioteca y rápido y sucio.

+0

solución mejor y más simple que he visto. – Nasser

2

El enfoque que utilizo normalmente dice así:

class = {} -- Will remain empty as class 
mt = {} -- Will contain everything the instances will contain _by default_ 

mt.new = function(self,foo) 
    local inst={} 
    if type(foo) == "table" then 
     for k,v in pairs(foo) do 
      inst[k]=v 
     end 
    else 
     inst.foo=foo 
    end 
    return setmetatable(inst,getmetatable(class)) 
end 

mt.print = function(self) 
    print("My foo is ",self.foo) 
end 

mt.foo= 4 --standard foo 

mt.__index=mt -- Look up all inexistent indices in the metatable 

setmetatable(class,mt) 

i1=class:new() -- use default foo 
i1:print() 

i2=class:new(42) 
i2:print() 

i3=class:new{foo=123,print=function(self) print("Fancy printing my foo:",self.foo) end} 

Bueno, conclusión: con metatablas y algo de conocimiento, sobre todo es posible: metatablas son la verdadera magia cuando se trabaja con las clases.

0

La mejor solución que vi es no implementar OO en Lua, donde no es natural y desigual, y por lo tanto toma muchas líneas; más bien, impleméntalo en C++ usando luabridge o luabind, ¡donde es natural y poderoso!

Un ejemplo minimalista que utiliza LuaBridge:

m.class_<MyClass>("MyClass") 
.constructor<void (*) (/* parameter types */)>() 
.method("method1", &MyClass::method1) 
.property_rw("property2", &MyClass::getter2, &MyClass::setter2) 
.property_ro("property3", &MyClass::property3) 

Esto se traduciría en la sintaxis lua natural:

c=MyClass() 
c.method1() 
c.property2 = c.property3 * 2 
do_stuff(c.property3) 

también es compatible inheritence de un nivel ...

+4

En mi humilde opinión, es un poco excesivo incluir C++ en un proyecto de lua para obtener OOP. Algunas personas podrían argumentar (incluido yo) que C++ OOP es en realidad muy intrincado, complejo y en realidad no es muy fiel a los principios originales de OOP. También el programador debería saber otro idioma para usarlo, no muy agradable ... aquí hay una buena lectura sobre C++ y su implementación de OO http://yosefk.com/c++fqa/class.html – lfzawacki

6

Esto ya es respondió, pero de todos modos, aquí está mi implementación OOP: middleclass.

Esa lib proporciona el mínimo indispensable para crear clases, instancias, herencia, polimorfismo y mixins (primitivos), con un rendimiento aceptable.

muestra:

local class = require 'middleclass' 

local Person = class('Person') 

function Person:initialize(name) 
    self.name = name 
end 
function Person:speak() 
    print('Hi, I am ' .. self.name ..'.') 
end 

local AgedPerson = class('AgedPerson', Person) -- or Person:subclass('AgedPerson') 

AgedPerson.static.ADULT_AGE = 18 --this is a class variable 
function AgedPerson:initialize(name, age) 
    Person.initialize(self, name) -- this calls the parent's constructor (Person.initialize) on self 
    self.age = age 
end 
function AgedPerson:speak() 
    Person.speak(self) -- prints "Hi, I am xx." 
    if(self.age < AgedPerson.ADULT_AGE) then --accessing a class variable from an instance method 
    print('I am underaged.') 
    else 
    print('I am an adult.') 
    end 
end 

local p1 = AgedPerson:new('Billy the Kid', 13) -- this is equivalent to AgedPerson('Billy the Kid', 13) - the :new part is implicit 
local p2 = AgedPerson:new('Luke Skywalker', 21) 
p1:speak() 
p2:speak() 

Salida:

Hi, I am Billy the Kid. 
I am underaged. 
Hi, I am Luke Skywalker. 
I am an adult. 
Cuestiones relacionadas