Vistas de página en total

jueves, 8 de septiembre de 2016

Programación orientada a objetos en Lua

Lua, al igual que otros lenguajes no está orientado a objetos. Otros lenguajes como Java o C# tienen palabras y construcciones especiales para definir clases, crear objetos, herencia, etc. Pero Lua no.
Los conocedores de javascript con un nivel elevado saben que javascript tampoco está orientado a objetos, pero que se pueden realizar algunas "artimañas" para conseguir algo similar a la orientación a objetos. En Lua ocurre exactamente los mismo: utilizando las funciones, y las tablas podemos conseguir una funcionalidad de orientación a objetos muy similar a la de otros lenguajes.
Es importante notar que no existe una única forma de conseguir OOP en Lua, sino que cada desarrollador suele tener una arquitectura, al igual que ocurre en javascript. Yo mostraré la que yo suelo utilizar, que no tiene porqué ser la mejor forma, pero que es muy sencilla de entender y a mí me ha servido durante muchos años.

Clase base

En primer lugar, definiremos una clase base en el fichero BaseClass.lua:

function BaseClass( param1, param2 )
    local this = {}
    local privateAttr = param1
    this.publicAttr = param2
    local function privateMethod()
        return privateAttr
    end
    this.publicMethod = function()
        return privateMethod()
    end
    return this
end

En este ejemplo, hemos definido una función constructora, BaseClass(), de una clase que tiene dos atributos: privateAttr (que es privado) y publicAttr (que es público). También se han definido dos métodos: privateMethod (que es privado) y publicMethod (que es público).
En primer lugar, se crea una tabla (llamada this, aunque valdría cualquier otro nombre). Luego se crean atributos en la tabla this (que serán los atributos públicos) y también funciones en la tabla (que serán las funciones públicas). Los atributos y funciones que sean local se convertirán en atributos y funciones privados.
Por último retornamos la tabla this que será el objeto que acabamos de crear con los atributos y métodos públicos.
En este momento, lo normal es hacerse la pregunta: ¿Qué ocurre con las variables y funciones local? ¿Desaparecen al retornar? Pues no, esas variables y funciones siguen existiendo en el closure de las funciones que hemos metido dentro de la tabla this, y por tanto, las funciones públicas podrán seguir accediendo a ellas una vez retornado this.

Clase derivada

Ahora vamos a definir una clase que hereda de la clase base, en el fichero DerivedClass.lua :

require ("BaseClass")

function DerivedClass( param1, param2, param3 )
    local this = ClaseBase(param1, param2)
    this.attr1 = param1
    this.attr2 = param2
    local attr3 = param3
    local function pp()
        print( this.attr1, attr3, this.publicMethod() ) 
    end
    this.increment = function()
        this.attr1 = this.attr1 + 1
        this.attr2 = this.attr2 + 1
    end
    return this
end

En primer lugar tenemos que hacer require del módulo BaseClass para tener acceso a la función constructora de la clase base.
En este módulo definimos otra función constructora, DerivedClass(), similar a la de la clase base. Pero hay algo distinto. En la clase base creábamos una tabla this vacía, y ahora estamos llamando a la función constructora de la clase base para que nos retorne this. A partir de ese momento, vamos añadiendo nuevos atributos y métodos a this, tal y como hicimos antes.
Terminamos, al igual que antes, retornando la tabla this con los atributos de la clase base y los de la clase derivada.

Utilización

Por último, vamos a utilizar la clase DerivedClass en un fichero main.lua:

require ( "DerivedClass")

local obj = DerivedClass( 1, 2, 3 )
obj.increment()
print(obj.publicAttr)
print(obj.attr1, obj.attr2)

local obj2 = DerivedClass( 4, 5, 6 )
obj2.increment()
print(obj2.publicAttr)
print(obj2.attr1, obj2.attr2)

No es difícil entender este main.lua. Lo primero que tenemos que hacer es la sentencia require para llegar a la clase derivada (no hace falta el require de la clase base, porque eso ya está dentro del módulo de la clase derivada).
Luego creamos objetos de la clase derivada, llamando a la función constructora. Y por último accedemos a sus atributos y métodos públicos.

Otras opciones para OOP en Lua

Como ya he dicho, esta es la forma en que yo hago orientación a objetos en Lua, pero existen muchas otras alternativas. Debido a que Lua no es orientado a objetos, entonces tenemos que "simular" esta característica con construcciones propias del lenguaje. Esta que he descrito es mi forma de hacerlo y hasta ahora me ha funcionado muy bien. De OOP lo que más necesito es la encapsulación, métodos públicos y privados, y la herencia. Con mi método consigo todo lo que necesito de una forma sencilla y que funciona.
Sin embargo, como también he dicho existen otras alternativas. Quizás una de las más conocidas sea la que propone la web de Lua:


También en Corona labs tienen una propuesta:


Y por último, en la wiki de Lua hay otro tutorial también interesante:


Con alguna de estas opciones tendremos características más avanzadas de la orientación a objetos en Lua, pero ya digo que en mi opinión no las he necesitado nunca, y el problema es que utilizan características avanzadas de Lua que pueden complicar el software que desarrollamos.










No hay comentarios:

Publicar un comentario