OOP in Lua and Lua Class Lib
原文链接 http://huiming.io/2011/06/01/lua-oop.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
This article assumes readers have a basic knowledge on Lua. And to understand the "Design and Implementation", a good Lua knowledge is required and some knowledge on Python OOP is a plus.
What's OOP on earth? How to do it in Lua? And what's Lua Class Lib? Let's have an interesting tour to OOP in Lua.
- 目录 {:toc}
A short story about OOP in Lua
"Lua is cool! But it lacks support for OOP!"
You may think that. Yes, I agree, if your criterion for OOP is just a "class" key word or something like that. It may disappoint you when you search for that in Lua official documents but find nothing you want.
The fact is Lua doesn't have a keyword for class, but Lua is really good at OOP.
Read on!
Object
Let's forget the superficial ideas of OOP and be after its essence:
What on earth does "object" mean?
In conception, it's just a thing that has properties and behaviors. In implementation, it is just a data structure saving properties, and some functions operating on it for behaviors. That's it!
According to the definition, Lua table is just a good data structure for OO:
t = {} --t is an object
t.status = 100 --t has a property
t.method = function(self) print self.status end --and a method
Class
Then what about the class?
In conception, it is simply a category: objects in the same category have something(properties and/or behaviors) in common. In implementation, it usually means some way to create an object of a category.
According to that, the object t
in above code does have a class: an anonymous category in our mind. You can create another object in the same category, or "class", by:
t2 = {} --t2 is another object of the same 'class' as t
t2.status = 101 --t2 has a different status
t2.method = function(self) print self.status end --and the same method
And, you can make the code reusable:
function createA(status)
local o = {}
o.status = status
o.method = function(self) print self.status end
return o
end
t1 = createA(100)
t2 = createA(101)
Inheritance
And what about inheritance?
Again, let's clear the definition first. In conception, inheritance means some relationship among classes: class B inheriting class A means B has something(properties and/or behaviors) in common with A, and thus objects of class B behave much like those of class A. In implementation, it usually means some code organization.
Having that in mind, we can say t3
in the following code has a conceptual class that inherits t2
's:
t3 = {}
t3.status = 10000
t3.method = function(self) print self.status end
t3.anotherStatus = 'hello'
t3.anotherMethod = function(self, n) self.status = self.status + n end
And we can make the code reusable:
function createB(status, anotherStatus)
local o = createA(status)
o.anotherStatus = anotherStatus
o.anotherMethod = function(self, n) self.status = self.status + n end
return o
end
t3 = createB(10000, 'hello')
That's the basic story of OOP in Lua. Not so appealing? I know you want more. Read on!
Lua Class Lib
But I want a more elegant way for OOP in Lua, as C++/Java/Python/... does.
I heard your heart and here's the Lua Class lib for your rescue.
Quick start
Here it is:
require 'cls' --import Lua Class Lib
class 'Person' --define a Class
{
__init__ = function(self, name) --define an initializer
self.name = name
end;
say = function(self) --define a method
print('Hello, my name is ' .. self.name .. '.')
self:saySthElse()
end;
saySthElse = function(self)
end
}
p = Person('Bob') --create an object
p:say() --call its method
Output:
Hello, my name is Bob.
Class inheritance:
class 'Employee: Person' --a class inheriting class Person
{
__init__ = function(self, name, salary, id)
Person.__init__(self, name) --call base class's method directly
self.salary = salary
end;
saySthElse = function(self) --override base class's method
print('My salary is ' .. self.salary .. '.')
end
}
e = Employee('Bob', 1000)
e:say()
Output:
Hello, my name is Bob.
My salary is 1000.
Even multiple inheritance:
class 'A' {...}
class 'B' {...}
class 'C: A, B' {...}
c = C()
assert(isInstanceOf(c, A))
assert(isInstanceOf(c, B))
assert(isInstanceOf(c, C))
Note that the class
in above code is in fact a user defined function, not a keyword of the language! Really cool, isn't it? Thanks to the powerful language!
Let's move on to the implementation for the class
function. Believe it or not: it's surprisingly short! Say it again: Lua is cool!
Design and Implementation
Lua doesn't provide a class keyword or something like that as other OO languages do, but Lua grants you much more power to shape your own OO implementation. The Lua Class Lib is just an example on how you can shape it. And it's also supposed to give you an idea of how powerful the language is.
The lib is inspired by and modeled on Python OOP. Although the two languages are quite different, it doesn't matter when it comes to the essence of OOP.
The major points are:
- A class is an object(table in Lua) of properties and methods, which is shared by all its instances, and its subclasses.
- An instance is also an object(table in Lua) of properties and methods.
- When referencing a member of an instance, the member is first looked up in the instance, then its class, and the first find is returned.
- When assigning to a member of an instance, the member is always stored in the instance.
- The class definition and instantiation "syntax" should be easy to understand.
- Keep it simple.
Here is the code behind the scenes:
local function parseName(str)
local _begin, _end, cls = assert(str:find('%s*([a-zA-Z][a-zA-Z0-9_]*)%s*%:?'))
if not str:find(':', _end) then
return cls, {}
end
local bases = {}
while true do
local base
_begin, _end, base = str:find('%s*([a-zA-Z][a-zA-Z0-9_]*)%s*%,?', _end + 1)
if base then
table.insert(bases, base)
else
break
end
end
return cls, bases
end
local function create(t, ...)
local o = {}
if t.__init__ then
t.__init__(o, ...)
end
return setmetatable(o, {__index = t, __class__ = t})
end
function class(name)
local env = getfenv(2)
local clsName, bases = parseName(name)
for i, v in ipairs(bases) do
bases[i] = env[v] --Replace string name with class table
end
return function (t)
local meta = {__call = create, __bases__ = bases}
meta.__index = function(nouse, key)
for _, cls in ipairs(meta.__bases__) do
local val = cls[key] --Do a recursive search on each cls
if val ~= nil then
return val
end
end
end
env[clsName] = setmetatable(t, meta)
end
end
The code is short but delicate, deserves a good read.
It's a minimal(and maybe adequate) implementation for OO in Lua, and there's always room to improve and extend it. However, before you do that, ask yourself "Do I really need that extension?" and "Will the extension make the whole system too complicated?" Have fun!