朱皮特的博客 自由的飞翔

Lua中实现类Class

2013-04-17
朱皮特
lua
阅读量:

原始代码:

local _class={}
 
function class(super)
	local class_type={}
	class_type.ctor=false
	class_type.super=super
	class_type.new=function(...) 
			local obj={}
			do
				local create
				create = function(c,...)
					if c.super then
						create(c.super,...)
					end
					if c.ctor then
						c.ctor(obj,...)
					end
				end
 
				create(class_type,...)
			end
			setmetatable(obj,{ __index=_class[class_type] })
			return obj
		end
	local vtbl={}
	_class[class_type]=vtbl
 
	setmetatable(class_type,{__newindex=
		function(t,k,v)
			vtbl[k]=v
		end
	})
 
	if super then
		setmetatable(vtbl,{__index=
			function(t,k)
				local ret=_class[super][k]
				vtbl[k]=ret
				return ret
			end
		})
	end
 
	return class_type
end

不足

这个设计有个不足之处是派生类函数中没法调用基类的同名函数。下面想办法改进下。

改进一(仅限于两层继承使用)

这段代码增加了一句:

obj.super = _class[superClass]

为的是在派生类中通过

self.super.xxx(self, ...)

的方式调用基类函数。但是仅限于两层继承,当多层继承时,例如 CBase ClassA ClassB ,在ClassB中调用self.super.inc是会调到ClassA中去,但是如果ClassA中继续调用self.super.inc, 就会不断在ClassA的当前函数中不断循环,因为self.super引用的是同一个表。因此不太建议使用这个方法。

例如如下代码片段就会有问题:

local _class={}
function class(superClass)
    local class_type={}
    class_type.ctor=false
    class_type.super=superClass
    class_type.new=function(...) 
            local obj={}
            setmetatable(obj,{ __index=_class[class_type] })
			obj.super = _class[superClass] --这里只适应于两层集成,多层集成,会有死循环。因为对象是同一个,会不断重复调用
            do
                local create
                create = function(Class, ...)
					local o = nil
                    if Class.super then
                        create(Class.super, ...)
                    end
                    if Class.ctor then
                        Class.ctor(obj, ...)
                    end
                end
 
                create(class_type, ...)
            end
            
            return obj
        end
    local vtbl={}
    _class[class_type]=vtbl
    
    setmetatable(class_type, {__newindex=
        function(t, funcName, funcAddr)
            vtbl[funcName] = funcAddr
        end
    })
 
    if superClass then
        setmetatable(vtbl,{__index=
            function(t,funcName)
				local funcAddr=_class[superClass][funcName]
				vtbl[funcName]=funcAddr
				return funcAddr
            end
        })
    end
	
 
    return class_type
end


-------------------------------------------------------
CBase = class()

function CBase:ctor(name)
	self.name = name
	self.x = 0
	self.y = 0
	print("CBase:ctor() x = "..tostring(self.x))
	print("CBase:ctor() y = "..tostring(self.y))
end
 
function CBase:show()
	print('CBase:show()')
	print("x = "..tostring(self.x))
	print("y = "..tostring(self.y))
end
 
function CBase:inc()
	print('CBase:inc()')
	self.x = self.x + 1
	self.y = self.y + 1
	print("x = "..tostring(self.x))
	print("y = "..tostring(self.y))
end

-------------------------------------------------------

ClassA = class(CBase)
 
function ClassA:ctor(name)
	self.name = name
	self.y = 2
	print("ClassA:ctor() y = "..tostring(self.y))
end
 
function ClassA:inc(n)
	n = n or 1
	self.super.inc(self, n)	--注意这里
	print("ClassA::inc()")
	self.y = self.y + n
	print('x = '..tostring(self.x))
	print('y = '..tostring(self.y))
end

-------------------------------------------------------

ClassB = class(ClassA)

function ClassB:inc(n)
	self.super.inc(self, n)	--
	print("ClassB::inc()")
end

a = ClassA.new('ClassA')
a:show()
a:inc()

print('--------')

b = ClassB.new('ClassB')
b:inc(4)	--这里调进去后会在ClassA:inc中死循环
print(b.name)
b:show()

改进二

只要在原始代码后面增加一小段代码:

local __super = _class
function super(class_type)
	return __super[class_type]
end

其实只要加一句:

function super(class_type)
    return _class[class_type]
end

使用__super代替_class只要是方便理解。

使用时便可以这样调用:

super(ClassA).inc(self, ...)

当然也可以使用以下任何一种:

__super[CBase].inc(self, ...)
_class[CBase]['inc'](self, ...)
_class[CBase].inc(self, ...)
__super[CBase]['inc'](self, ...)
__super[CBase].inc(self, ...)

修改后的

--基类
local _class={}
function class(superClass)
    local class_type={}
    class_type.ctor=false
    class_type.super=superClass
    class_type.new=function(...) 
            local obj={}
            setmetatable(obj,{ __index=_class[class_type] })
			--obj.super = _class[superClass],这里只适应于两层集成,多层集成,会有死循环。因为对象是同一个,会不断重复调用
            do
                local create
                create = function(Class, ...)
					local o = nil
                    if Class.super then
                        create(Class.super, ...)
                    end
                    if Class.ctor then
                        Class.ctor(obj, ...)
                    end
                end
 
                create(class_type, ...)
            end
            
            return obj
        end
    local vtbl={}
    _class[class_type]=vtbl
    
    setmetatable(class_type, {__newindex=
        function(t, funcName, funcAddr)
            vtbl[funcName] = funcAddr
        end
    })
 
    if superClass then
        setmetatable(vtbl,{__index=
            function(t,funcName)
				local funcAddr=_class[superClass][funcName]
				vtbl[funcName]=funcAddr
				return funcAddr
            end
        })
    end
	
 
    return class_type
end


local __super = _class
function super(class_type)
	return __super[class_type]
end

示例代码

CBase = class()  -- 定义一个基类 CBase

function CBase:ctor(name) -- 定义 CBase 的构造函数
	self.name = name
	self.x = 0
	self.y = 0
	print("CBase:ctor() x = "..tostring(self.x))
	print("CBase:ctor() y = "..tostring(self.y))
end
 
function CBase:show() -- 定义一个成员函数 CBase:show
	print('CBase:show()')
	print("x = "..tostring(self.x))
	print("y = "..tostring(self.y))
end
 
function CBase:inc() -- 定义另一个成员函数 CBase:inc
	print('CBase:inc()')
	self.x = self.x + 1
	self.y = self.y + 1
	print("x = "..tostring(self.x))
	print("y = "..tostring(self.y))
end

-------------------------------------------------------

ClassA = class(CBase) -- 定义一个类 ClassA 继承于 CBase
 
function ClassA:ctor(name) -- 定义 ClassA 的构造函数
	self.name = name
	self.y = 2
	print("ClassA:ctor() y = "..tostring(self.y))
end
 
function ClassA:inc(n) -- 重载 CBase:inc 为 ClassA:inc
	n = n or 1
	__super[CBase].inc(self, n)
	--_class[CBase]['inc'](self, n)
	--_class[CBase].inc(self, n)
	print("ClassA::inc()")
	--print(_class[CBase]:inc(self))
	self.y = self.y + n
	print('x = '..tostring(self.x))
	print('y = '..tostring(self.y))
end

-------------------------------------------------------

ClassB = class(ClassA)

function ClassB:inc(n)
	--__super[ClassA].inc(self, n)	--u can try:__super[CBase].inc(self, n)
	super(ClassA).inc(self, n)
	--_class[ClassA]['inc'](self, n)
	--_class[ClassA].inc(self, n)
	print("ClassB::inc()")
end

a = ClassA.new('ClassA')
a:show()
a:inc()

print('--------')

b = ClassB.new('ClassB')
b:inc(4)
print(b.name)
b:show()

-----------------


BaseController = class()


function BaseController:ctor()
    self.mLayer = nil

    --self.mLayer = cc.Layer:create()
	self.mLayer = 'haha'
end

function BaseController:create()

end

function BaseController:remove()
    if self.mLayer then
        --self.mLayer:removeFromParent(true)
        self.mLayer = nil
    end
end

function BaseController:getLayer()
    return self.mLayer
end

Test = class(BaseController)

function Test:create()
	print('-----------')
	super(BaseController).create(self)
	print(self.mLayer)
	print(self:getLayer(Test))
end


test = Test.new()

test.fun = function()
	print('fun')
end

test:create()
print(test:getLayer())
test.fun()

使用技巧

实际使用方法:

  • 1、派生类的构造函数(ctor)可以声明也可以不声明。
  • 2、类的函数声明的时候用冒号。
  • 3、如果想用表,可以这样结合使用: ```lua Test = class(BaseController) –声明一个派生类

function Test:create() –类函数,用冒号声明,里面可以使用self print(‘———–’) self.x = 1 end

test = Test.new() –此时test是一个表

test.fun = function() –此时fun是test表的一个字段,使用时就要和类函数区别开来,就要用点的形式,这个函数里不能使用self print(‘fun’) –可以看做是静态函数。 end

test:create() –这个调用的是类函数,一定要用冒号 print(test:getLayer()) test.fun() –调用表的一个字段函数,用点

return test –在一个Lua文件中返回test的好处是既可以当做类对象使用(调用函数用冒号),又可以使用表里面的函数(调用函数用点)。 ```

参考


下一篇 如何编译duilib

Comments

Content