Lua学习笔记(五):面向对象的实现

Lua本身是没有class之类的关键字的,但是我们可以巧妙利用function也是值和table的特性来实现面向对象的特性。

通过复制表的实现

Lua中的类也是一个table对象,下面我们看看一个简单的类的定义:

 1 People = {}--定义表 People
 2 
 3 --添加方法的第一种方式
 4 People.talk = function()
 5   print("people talk.")
 6 end
 7 
 8 --添加方法的第二种方式
 9 function People.say()
10   print("people say.")
11 end
12 
13 People.talk()--people talk.
14 People.say()--people say.

我们发现添加一个方法有两种方式,这两种方式的效果都是一致的,至于使用哪种就看个人喜好了。

通过拷贝创建实例

不知道大家发现没有,上面的示例中并没有创建实例这个步骤,而在面向对象中,类是需要创建实例来使用的,下面我们就用复制的方法来创建实例:

 1 --克隆一个表
 2 function clone(obj)
 3   local instance = {}
 4   
 5   for key, var in pairs(obj) do
 6       instance[key] = var
 7   end
 8   
 9   return instance;
10 end
11 
12 People = {}--定义表 People
13 
14 --定义方法
15 People.talk = function()
16   print("people talk.")
17 end
18 
19 --创建 People 的实例
20 local p = clone(People)
21 p.talk()--people talk.

构造函数

我们发现虽然可以创建实例了,但是却没有构造函数,下面我们把构造函数也添加进去:

 1 --克隆一个表
 2 function clone(obj)
 3   local instance = {}
 4   
 5   for key, var in pairs(obj) do
 6       instance[key] = var
 7   end
 8   
 9   return instance;
10 end
11 
12 People = {}--定义表 People
13 
14 --定义构造函数
15 People.new = function(name)
16   local ins = clone(People)
17   ins.name = name;
18   return ins
19 end 20 21 --定义方法 22 People.talk = function(self) 23 print(self.name.." talk.") 24 end 25 26 --创建 People 的实例 27 local p = People.new("Li Lei") 28 p.talk(p)--people talk. 29 p:talk()--使用 : 号可以省略 self 的填写, 效果和上一行一致

这里引入了一个新的变量self和“:”符号,下面我们来说说:

self

self作为第一个参数表示调用则本身。

.号和:号

调用时为了传递调用者本身,所以第一个参数需要将自身传入,这是.号的写法;

如果使用:号,则默认第一个参数传入调用者本身,无需我们再写第一个参数了,我们可以从第二个参数开始写,但是函数声明时仍然要写第一个参数self的。

定义函数时也可以使用:号进行定义,这样定义方法时也可以省略self参数,但是写法只能写成function tab:foo() end这样的形式,如下:

 1 obj = {name = "Li Lei"}
 2 --.号的写法和调用
 3 obj.foo1 = function()
 4   print "foo1"
 5 end
 6 function obj.foo2(self)
 7   print(self.name.."foo2")
 8 end
 9 obj.foo1()--foo1
10 obj.foo2(obj)--Li Leifoo2
11 --:号的写法和定义
12 --[[注释掉的这种写法是不允许的
13 obj:foo3 = function()
14   print(self.name.."foo3")
15 end
16 ]]
17 function obj:foo4()
18   print(self.name.."foo4")
19 end
20 obj:foo4()--Li Leifoo4

继承

下面我们来看看如何实现类的继承:

 1 --克隆一个表
 2 function clone(obj)
 3   local instance = {}
 4   
 5   for key, var in pairs(obj) do
 6       instance[key] = var
 7   end
 8   
 9   return instance;
10 end
11 
12 People = {}--定义表 People
13 
14 --定义构造函数
15 People.new = function(name)
16   local ins = clone(People)
17   ins.name = name;
18   return ins
19 end
20 
21 --定义方法
22 People.talk = function(self)
23   print(self.name.." talk.")
24 end
25 
26 --定义方法2
27 People.talk2 = function(self)
28   print(self.name.." talk2.")
29 end
30 
31 --创建 People 的实例
32 local p = People.new("Li Lei")
33 p.talk(p)--people talk.
34 p:talk()--使用 : 号可以省略 self 的填写, 效果和上一行一致
35 
36 --下面是关于继承的实现
37 
38 --将 origin 表的值复制到 dist 表中
39 function copy(origin, dist)
40   for key, var in pairs(origin) do
41       dist[key] = var
42   end
43 end
44 
45 Man = {}--定义表 Man, 继承自 People
46 
47 Man.new = function(name)
48   local ins = People.new(name)--创建继承的类, 这样就拥有了继承类的所有属性
49   copy(Man, ins)--将 Man 的属性附加到实例
50   return ins
51 end
52 
53 --定义方法
54 Man.say = function()
55   print("Man say!")
56 end
57 
58 --重写方法
59 Man.talk2 = function(self)
60   print(self.name.." talk2. (Man)")
61 end
62 
63 --创建 Man 的实例
64 local m = Man.new("Han Meimei")
65 m:talk()--Han Meimei talk.
66 m:talk2()--Han Meimei talk2. (Man)
67 m:say()--Man say!

简单来说,就是先创建父类的实例,然后把新类的所有属性都拷贝到刚创建的实例上即可。

通过函数闭包的实现

我们可以通过在函数内定义函数的方式来实现面向对象的特性:

 1 --定义方法直接返回表的实例
 2 function People(name)
 3   local ins = {}--创建表
 4   
 5   --初始化函数
 6   local function init()
 7     ins.name = name
 8   end
 9   
10   --定义方法
11   ins.talk = function()
12     print(ins.name.." talk.")
13   end
14   
15   --定义带参数的方法
16   ins.talk2 = function(content)
17     print(ins.name.." talk "..content..".")
18   end
19   
20   -- self 可以省去了, 因为可以通过闭包的特性获取到, 或者如果要统一 : 符号可以这么写
21   ins.talk3 = function(self, content)
22     print(self.name.." talk "..content..".")
23   end
24   
25   init()--调用初始化函数
26   
27   return ins
28 end
29 
30 --创建对象
31 p = People("Li Lei")
32 p:talk()--没有参数可以这么写
33 p.talk()
34 
35 --p:talk2("hello")--没有 self 参数不能这么调用了
36 p.talk2("hello")
37 
38 p:talk3("hello 2")
39 p.talk3(p, "hello 2")

这种写法的运行效率略低于上一种方法,但是却更加清晰,在实际使用时推荐使用该方法来编写类。

继承

使用这种写法也可以实现类的继承,如下:

 1 --定义方法直接返回表的实例
 2 function People(name)
 3   local ins = {}--创建表
 4   
 5   --初始化函数
 6   local function init()
 7     ins.name = name
 8   end
 9   
10   --定义方法
11   ins.talk = function()
12     print(ins.name.." talk.")
13   end
14   
15   --定义带参数的方法
16   ins.talk2 = function(content)
17     print(ins.name.." talk "..content..".")
18   end
19   
20   -- self 可以省去了, 因为可以通过闭包的特性获取到, 或者如果要统一 : 符号可以这么写
21   ins.talk3 = function(self, content)
22     print(self.name.." talk "..content..".")
23   end
24   
25   init()--调用初始化函数
26   
27   return ins
28 end
29 
30 --下面是继承的代码
31 
32 function Man(name)
33   local ins = People(name)--关键: 创建一个 People 类的实例
34   
35   --添加新项目
36   ins.say = function()
37     print(ins.name.." say.")
38   end
39   
40   --覆写老方法
41   ins.talk = function()
42     print(ins.name.." talk. (Man)")
43   end
44   
45   return ins
46 end
47 
48 m = Man("Han Meimei")
49 m.say()--Han Meimei say.
50 m.talk()--Han Meimei talk. (Man)

其实就是创建父类的对象后添加新的属性,比较容易理解。

原文地址:https://www.cnblogs.com/hammerc/p/4709133.html