2009-07-07 17 views
6

Estoy teniendo dificultades para formar clases en Lua. Google infructuoso me llevó a ideas sobre meta-tablas e implicaba que las bibliotecas de terceros son necesarias para simular/escribir clases.¿Cómo crear una clase, una subclase y propiedades en Lua?

He aquí una muestra (sólo porque me he dado cuenta de que obtener mejores respuestas cuando proporciono código de ejemplo):

public class ElectronicDevice 
{ 
    protected bool _isOn; 
    public bool IsOn { get { return _isOn; } set { _isOn = value; } } 
    public void Reboot(){_isOn = false; ResetHardware();_isOn = true; } 
} 

public class Router : ElectronicDevice 
{ 
} 

public class Modem :ElectronicDevice 
{ 
    public void WarDialNeighborhood(string areaCode) 
    { 
     ElectronicDevice cisco = new Router(); 
     cisco.Reboot(); 
     Reboot(); 
     if (_isOn) 
      StartDialing(areaCode); 
    } 
} 

Aquí es mi primer intento de traducir lo anterior utilizando la técnica sugerida por Javier.

Seguí el consejo de RBerteig. Sin embargo, las invocaciones en las clases derivadas todavía Rendimiento: "attempt to call method 'methodName' (a nil value)"

--Everything is a table 
ElectronicDevice = {}; 

--Magic happens 
mt = {__index=ElectronicDevice}; 

--This must be a constructor 
function ElectronicDeviceFactory() 
    -- Seems that the metatable holds the fields 
    return setmetatable ({isOn=true}, mt) 
end 

-- Simulate properties with get/set functions 
function ElectronicDevice:getIsOn() return self.isOn end 
function ElectronicDevice:setIsOn(value) self.isOn = value end 
function ElectronicDevice:Reboot() self.isOn = false; 
    self:ResetHardware(); self.isOn = true; end 
function ElectronicDevice:ResetHardware() print('resetting hardware...') end 

Router = {}; 
mt_for_router = {__index=Router} 

--Router inherits from ElectronicDevice 
Router = setmetatable({},{__index=ElectronicDevice}); 

--Constructor for subclass, not sure if metatable is supposed to be different 
function RouterFactory() 
    return setmetatable ({},mt_for_router) 
end 

Modem ={}; 
mt_for_modem = {__index=Modem} 

--Modem inherits from ElectronicDevice 
Modem = setmetatable({},{__index=ElectronicDevice}); 

--Constructor for subclass, not sure if metatable is supposed to be different 
function ModemFactory() 
    return setmetatable ({},mt_for_modem) 
end 

function Modem:WarDialNeighborhood(areaCode) 
     cisco = RouterFactory(); 
     --polymorphism 
     cisco.Reboot(); --Call reboot on a router 
     self.Reboot(); --Call reboot on a modem 
     if (self.isOn) then self:StartDialing(areaCode) end; 
end 

function Modem:StartDialing(areaCode) 
    print('now dialing all numbers in ' .. areaCode); 
end 

testDevice = ElectronicDeviceFactory(); 
print("The device is on? " .. (testDevice:getIsOn() and "yes" or "no")); 
testDevice:Reboot(); --Ok 

testRouter = RouterFactory(); 
testRouter:ResetHardware(); -- nil value 

testModem = ModemFactory(); 
testModem:StartDialing('123'); -- nil value 
+0

¿Leyó [esto] (http://lua-users.org/wiki/SimpleLuaClasses)? – ThibThib

+0

Sí y me pareció opaco. – MatthewMartin

Respuesta

8

Aquí hay un ejemplo de transcripción literal de su código, con una útil biblioteca Class que podría moverse a otro archivo.

Esto de ninguna manera es una implementación canónica de Class; siéntete libre de definir tu modelo de objetos como quieras.

Class = {} 

function Class:new(super) 
    local class, metatable, properties = {}, {}, {} 
    class.metatable = metatable 
    class.properties = properties 

    function metatable:__index(key) 
     local prop = properties[key] 
     if prop then 
      return prop.get(self) 
     elseif class[key] ~= nil then 
      return class[key] 
     elseif super then 
      return super.metatable.__index(self, key) 
     else 
      return nil 
     end 
    end 

    function metatable:__newindex(key, value) 
     local prop = properties[key] 
     if prop then 
      return prop.set(self, value) 
     elseif super then 
      return super.metatable.__newindex(self, key, value) 
     else 
      rawset(self, key, value) 
     end 
    end 

    function class:new(...) 
     local obj = setmetatable({}, self.metatable) 
     if obj.__new then 
      obj:__new(...) 
     end 
     return obj 
    end 

    return class 
end 

ElectronicDevice = Class:new() 

function ElectronicDevice:__new() 
    self.isOn = false 
end 

ElectronicDevice.properties.isOn = {} 
function ElectronicDevice.properties.isOn:get() 
    return self._isOn 
end 
function ElectronicDevice.properties.isOn:set(value) 
    self._isOn = value 
end 

function ElectronicDevice:Reboot() 
    self._isOn = false 
    self:ResetHardware() 
    self._isOn = true 
end 

Router = Class:new(ElectronicDevice) 

Modem = Class:new(ElectronicDevice) 

function Modem:WarDialNeighborhood(areaCode) 
    local cisco = Router:new() 
    cisco:Reboot() 
    self:Reboot() 
    if self._isOn then 
     self:StartDialing(areaCode) 
    end 
end 

Si se va a pegarse a obtener/métodos set para las propiedades, usted no necesitaría __index y __newindex funciones, y sólo podía tener una mesa de __index. En ese caso, la forma más fácil para simular la herencia es algo como esto:

BaseClass = {} 
BaseClass.index = {} 
BaseClass.metatable = {__index = BaseClass.index} 

DerivedClass = {} 
DerivedClass.index = setmetatable({}, {__index = BaseClass.index}) 
DerivedClass.metatable = {__index = DerivedClass.index} 

En otras palabras, __index la mesa de la clase derivada "hereda" mesa __index de la clase base.Esto funciona porque Lua, al delegar a una tabla __index, repite efectivamente la búsqueda en él, por lo que se invocan los meta-métodos de la tabla __index.

Además, tenga cuidado al llamar al obj.Method(...) frente al obj:Method(...). obj:Method(...) es azúcar sintáctico para obj.Method(obj, ...), y mezclar las dos llamadas puede producir errores inusuales.

6

Hay un número de maneras en que puede hacerlo, pero esto es como lo hago (actualizado con un tiro en la herencia):

function newRGB(r, g, b) 
    local rgb={ 
     red = r; 
     green = g; 
     blue = b; 
     setRed = function(self, r) 
      self.red = r; 
     end; 
     setGreen = function(self, g) 
      self.green= g; 
     end; 
     setBlue = function(self, b) 
      self.blue= b; 
     end; 
     show = function(self) 
      print("red=",self.red," blue=",self.blue," green=",self.green); 
     end; 
    } 
    return rgb; 
end 

purple = newRGB(128, 0, 128); 
purple:show(); 
purple:setRed(180); 
purple:show(); 

---// Does this count as inheritance? 
function newNamedRGB(name, r, g, b) 
    local nrgb = newRGB(r, g, b); 
    nrgb.__index = nrgb; ---// who is self? 
    nrgb.setName = function(self, n) 
     self.name = n; 
    end; 
    nrgb.show = function(self) 
     print(name,": red=",self.red," blue=",self.blue," green=",self.green); 
    end; 
    return nrgb; 
end 

orange = newNamedRGB("orange", 180, 180, 0); 
orange:show(); 
orange:setGreen(128); 
orange:show(); 

I no implementar privado, protegido, etc. although it is possible.

1

Es realmente fácil hacer una POO de clase en Lua; sólo hay que poner todos los 'métodos' en el campo de metatabla __index:

local myClassMethods = {} 
local my_mt = {__index=myClassMethods} 

function myClassMethods:func1 (x, y) 
    -- Do anything 
    self.x = x + y 
    self.y = y - x 
end 

............ 

function myClass() 
    return setmetatable ({x=0,y=0}, my_mt) 

herencia Personalmente, nunca he necesitado, por lo que lo anterior es suficiente para mí. Si no es suficiente, se puede establecer un metatabla para la tabla de métodos:

local mySubClassMethods = setmetatable ({}, {__index=myClassMethods}) 
local my_mt = {__index=mySubClassMethods} 

function mySubClassMethods:func2 (....) 
    -- Whatever 
end 

function mySubClass() 
    return setmetatable ({....}, my_mt) 

actualización: Hay un error en el código de actualización:

Router = {}; 
mt_for_router = {__index=Router} 
--Router inherits from ElectronicDevice 
Router = setmetatable({},{__index=ElectronicDevice}); 

Tenga en cuenta que inicializar Router, y construir mt_for_router de esto; pero luego reasigna Router a una nueva tabla, mientras que mt_for_router aún apunta al original Router.

Reemplace el Router={} con el Router = setmetatable({},{__index=ElectronicDevice}) (antes de la inicialización mt_for_router).

3

La forma en que me gustaba hacerlo era mediante la implementación de una función clone().
Tenga en cuenta que esto es para Lua 5.0. Creo que 5.1 tiene más construcciones orientadas a objetos incorporadas.

clone = function(object, ...) 
    local ret = {} 

    -- clone base class 
    if type(object)=="table" then 
      for k,v in pairs(object) do 
        if type(v) == "table" then 
          v = clone(v) 
        end 
        -- don't clone functions, just inherit them 
        if type(v) ~= "function" then 
          -- mix in other objects. 
          ret[k] = v 
        end 
      end 
    end 
    -- set metatable to object 
    setmetatable(ret, { __index = object }) 

    -- mix in tables 
    for _,class in ipairs(arg) do 
      for k,v in pairs(class) do 
        if type(v) == "table" then 
          v = clone(v) 
        end 
        -- mix in v. 
        ret[k] = v 
      end 
    end 

    return ret 
end 

A continuación se define una clase como una tabla:

Thing = { 
    a = 1, 
    b = 2, 
    foo = function(self, x) 
    print("total = ", self.a + self.b + x) 
    end 
} 

instanciarlo o para derivar de ella, se utiliza clone() y se puede anular cosas haciéndolas pasar en otra tabla (o tablas) como mezcla-ins

myThing = clone(Thing, { a = 5, b = 10 }) 

llamar, se utiliza la sintaxis:

myThing:foo(100); 

que imprimirá:

total = 115 

para derivar una sub-clase, que, básicamente, definir otro objeto prototipo:

BigThing = clone(Thing, { 
    -- and override stuff. 
    foo = function(self, x) 
     print("hello"); 
    end 
} 

Este método es muy simple, posiblemente demasiado simple, pero funcionó bien para mi proyecto.

1

Su código actualizado es prolijo, pero debería funcionar. Excepto, que tienen un error tipográfico que está rompiendo una de las metatablas:

 
--Modem inherits from ElectronicDevice 
Modem = setmetatable({},{__index,ElectronicDevice}); 

debe leer

 
--Modem inherits from ElectronicDevice 
Modem = setmetatable({},{__index=ElectronicDevice}); 

El fragmento existente hizo que el Modem metatabla ser una matriz en donde el primer elemento era casi con toda seguridad nula (el valor habitual de _G.__index a menos que esté usando strict.lua o algo similar) y el segundo elemento es ElectronicDevice.

La descripción Lua Wiki tendrá sentido una vez que haya agregado metatablas un poco más. Una cosa que ayuda es a construir una pequeña infraestructura para hacer que los patrones habituales sean más fáciles de corregir.

También recomendaría leer el capítulo sobre OOP en PiL. También querrá volver a leer los capítulos sobre tablas y metatablas. Además, me he vinculado a la copia en línea de la primera edición, pero poseer una copia de la segunda es muy recomendable. También hay un par de artículos en el libro Lua Gems que se relacionan. También es recomendado.

4

Si no desea reinventar la rueda, existe una buena biblioteca Lua que implementa varios modelos de objetos. Se llama LOOP.

1

Otro enfoque simple para subclase

local super = require("your base class") 
local newclass = setmetatable({}, {__index = super }) 
local newclass_mt = { __index = newclass } 

function newclass.new(...) -- constructor 
    local self = super.new(...) 
    return setmetatable(self, newclass_mt) 
end 

Puede seguir utilizando las funciones de superclase, incluso si se sobrescriben

function newclass:dostuff(...) 
    super.dostuff(self,...) 
    -- more code here -- 
end 

no se olvide de usar un punto al pasar el auto a la función superclase

Cuestiones relacionadas