I've discovered (and written) a way to object oriented programming in lua in a functional fashion instead of the standard metatable method. It carries much more functionality (no pun intended) but I'm afraid it might dent performance. My goal is to experiment with servers written in lua and I wanted to use this OOP solution. Anyways here's my class.lua which holds the functions for creating new objects and subclasses.
local classes = setmetatable({}, {__mode = "k"}) -- Allow the GC to empty this as needed.
function class(f, super)
classes[f] = {super = super}
end
function new(f, obj, ...)
local fenv = getfenv(f)
if type(obj) ~= "table" then
error("bad argument: expected table, got " .. type(obj) , 2)
end
if classes[f] and classes[f].super then
new(classes[f].super, obj, ...)
local super = obj
obj = setmetatable({}, { __index = super })
obj.super = super
else
setmetatable(obj,{__index = fenv})
end
obj.this = obj
setfenv(f, obj)
f()
setfenv(f, fenv)
if obj.init then
obj.init( ... )
end
return obj
end
Usage is fairly simple. Take the following example:
function Person()
local privateVar = math.random()
age, name, gender = nil, nil, nil
function init(age, name, gender)
this.age = age
this.name = name
this.gender = gender
end
function getPrivateVar()
return privateVar
end
end
And to create the object
obj = new(Person, {}, "John", 30, "male")
Subclassing is also simple
function Female()
function init(name, age)
super.init(name, age, "female")
end
end
class(Female, Person)
Note that you only have to call a function to make something a class if you want to subclass.
When you call new, you pass in the class, object, and arguments. The object's metatable is set to have an index of the class's original environment. Then the super is determined and created. Then the class function is run to create all instance values. Next, init is called.
One advantage of this over metatables are that other code can't change the class and have it change all instances of that class that already exist and that will exist. Now the only way to do something like that is to swizzle and hack around in environments to make sure all references to the class refer to the hacker's dummy class.
On top of that, it supports private variables. Just declare them locally in your class function and you're good to go.
But I'm wondering if the fact that it creates all the instance methods once for every instantiation is going to cause performance problems. Will this eat memory? What can go wrong here?
Also, there's an issue. While this supports multiple inheritance, if you call a super method, and the super calls a method, it will call the method of the super class, not the instantiated class. Any ideas how to make it call from the instantiated class?