Dorothy面向对象的写法

  用Lua编程是非常自由的。闭包、table加上metatable,可以按自己的喜好调教成任意的编程风格。我最喜欢函数式的写法,在写乱七八糟的业务逻辑的时候,可以一边写一边想,想到什么业务规则就随手加什么业务规则,开发效率很高。当然写的时候行云流水很顺畅,写完要维护会费一些力。示例比如这个动画编辑器的时间轴控件,支持动态地无限延伸,上面的进度条可以拖拽,双击时间轴可以使进度条跳跃至双击位置,点击控件时会高亮,停止互动后会变暗,写的时候真的是想到什么功能就随手加上什么功能,几乎不要事先设计代码结构,可以在这里随便看一眼这种风格。
  当然也不是谁都hold住写得这么随意的代码。我是从C++面向对象开始学编程的,也曾经不面向对象不知道怎么写程序。类、继承、方法重写、静态方法等等,没有这些语言特性还怎么愉快地耍设计模式?所以还是给Dorothy的Lua加了一套面向对象的机制,虽然现在觉得可用可不用,还是介绍一下。
  Dorothy中通过Lua binding导入Lua的C++对象,有一个问题就是Lua方法中无法真正覆盖C++对象的方法(Lua调C++容易,C++调Lua困难),所以无法对C++对象做严格意义上面向对象的继承。所以如果是想要写一个自己的Lua类“继承”C++导入的类,只能是通过对C++类的功能做扩展的方式。

lua class

扩展Lua中的C++对象的方法

  Lua中的C++对象,其实是用Lua Userdata包装而来的。访问这个Userdata对象的时候,首先会访问C++对象导入到Lua中的接口,如果访问不到请求的内容,就会接着访问Userdata的环境表Env table,这个环境表是一个可以自定义的Lua table。我们扩展的内容就可以添加到这个环境表里来。这样的结构带来的不好的地方,就是在Lua中无法覆盖原C++对象提供的接口(反正也没有办法真正的覆盖)。Lua中的Userdata对象可以通过`tolua.setpeer`方法来直接设置环境表,或是使用Dorothy中提供的class机制。Dorothy支持创建纯lua的类和对象,或是扩展C++导入的类和对象,语法和python的class有一点点相似。除了扩展C++的类要特别提供一个__partial的方法,并在方法中要返回一个创建C++对象,其它的写法都比较自然。 ##代码示例   以下例子是一个自定义的Button类,和继承Button的CheckButton类,Button类扩展自CCMenuItem。 ```lua Dorothy() -- import Dorothy lib to current environment

local class,property,classfield,classmethod = unpack(require("class"))

local Button -- make Button an upper value for class init function to capture
Button = class(
{
__initc = function(cls) -- init class
cls._itemCount = 0 -- count created button numbers
end,

ItemCount = classfield(function(cls) -- class member readonly
	return cls._itemCount
end),

displayInfo = classmethod(function(cls) -- class method
	print("Button Count:", cls.ItemCount)
end),

-- partially implement CCMenuItem
__partial = function(self,text,width,height,callback)
	-- params passed but not used
	self._label = nil
	return CCMenuItem() -- create and return C++ instance
end,

__init = function(self,text,width,height,callback) -- init instance
	local halfW = width*0.5
	local halfH = height*0.5

	-- self is also a CCMenuItem instance
	self.contentSize = CCSize(width,height)
	self.opacity = 0.4
	
	local border = CCDrawNode()
	border:drawPolygon(
	{
		oVec2(-halfW,halfH),
		oVec2(halfW,halfH),
		oVec2(halfW,-halfH),
		oVec2(-halfW,-halfH),
	},ccColor4(0x88ff0088),0.5,ccColor4())
	border.position = oVec2(width*0.5,height*0.5)
	self:addChild(border)
	
	local label = CCLabelTTF(text,"Arial",16)
	label.position = oVec2(width*0.5,height*0.5)
	self:addChild(label)
	self._label = label
	
	self.tapHandler = function(eventType)
		if eventType == CCMenuItem.TapBegan then
			self.opacity = 1
		elseif eventType == CCMenuItem.TapEnded then
			self.opacity = 0.4
		elseif eventType == CCMenuItem.Tapped then
			if callback then
				callback(self)
			end
		end
	end

	Button._itemCount = Button._itemCount + 1 -- increase button count
end,

displayType = function(self)
	print(self.text) -- access text property
end,

-- init text property with getter and setter
text = property(
	function(self)
		return self._label.text
	end,
	function(self,value)
		self._label.text = value
	end),

})

local CheckButton = class(Button,
{
__partial = function(self)
self.checked = false
return Button.__partial(self)
end,

__init = function(self,onText,offText,width,height,callback)
	Button.__init(self,offText,width,height,
		function()
			self.checked = not self.checked
			self.text = self.checked and onText or offText
			if callback then
				callback(self)
			end
		end)
end,

displayType = function(self) -- override super method
	Button.displayType(self) -- call method from super
	print("Check State:", self.checked)
end,

})

local winSize = CCDirector.winSize
local center = oVec2(winSize.width0.5,winSize.height0.5)

local scene = CCScene()

local menu = CCMenu()
menu.position = center
scene:addChild(menu)

local button = Button("Click\nMe",60,60,
function(button)
button:displayType() -- call method from Button
end)
button.position = center-oVec2(100,0)
menu:addChild(button)

local checkButton = CheckButton("Switch\nOn","Switch\nOff",60,60,
function(button)
button:displayType() -- call method from CheckButton
end)
checkButton.position = center+oVec2(100,0)
menu:addChild(checkButton)

Button:displayInfo() -- call class method from Button

CCDirector:run(scene)

> Dorothy的class机制的实现源码见:[这里](http://git.oschina.net/pig/Dorothy/blob/master/project/Resources/Lib/class.lua)。
标题目录