Lua 中继承与多态继承的实现
- 引言
- 目标
- 背景知识
- 类的特性
- 原表
- 实现
- 类 - 封装
- 继承类 - 继承与多态
- 继承类 - 多重继承
- Reference
引言
在 lua 中并没有类的概念。但是 lua 有一个数据结构 table
,我们可以基于 table
数据结构来实现类似于 C++ 中的类。
目标
- 实现一个类似其他语言中的类的
class
- 支持类与类之间的继承
- 支持类之间的多重继承
背景知识
类的特性
- 封装:面向对象编程与面向过程编程最大的区别。将客观事务封装成抽象的类
- 继承:面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
- 多态:同一个行为具有多个不同表现形式或形态的能力。是指一个类实例(对象)的相同方法在不同情形有不同表现形式。
原表
Lua 中的原表(metatable) : Lua metatable
实现
类 - 封装
最开始,我们不考虑继承多态等问题,我们首先实现一个类。最直观的特征,类应该包含一些自己的数据与方法。
首先我们实现最初版本的 class
函数。main
中的代码展示了如何调用我们编写的 class
来生成一个类。
-- function class, used for defing a new class
local class = function ( class_name )
if type(class_name) ~= "string" then
error("class name must be string")
end
local cls = {__class_name = class_name}
-- 默认构造函数 do nothing
cls.ctor = function ( ... )
print("default ctor function")
end
cls.new = function ( ... )
local o = {}
setmetatable(o, {__index = cls})
o.__class = cls
o.ctor(...)
return o
end
return cls
end
function main()
local animal = class("animal")
-- 重写构造函数,为 self.name 赋值
function animal:ctor( ... )
print("constructor an animal ", ...)
self.name = ...
end
function animal:run()
print(string.format("%s is running.", self.name))
end
local animal_1 = animal:new("animal_1")
animal_1:run()
local animal_2 = animal:new("animal_2")
animal_2:run()
end
main()
-- output:
constructor an animal animal_1
animal_1 is running.
constructor an animal animal_2
animal_2 is running.
NOITCE: 这里没有实现析构函数,各位看官可自行实现。
继承类 - 继承与多态
通过上一步,我们已经有一个类的雏形了。现在我们在上文 class
函数的基础上再做改造,来实现一个支持继承的类,同时它还支持多态。
NOTICE: 此部分基于 metatable 实现,请务必了解 metatable 的用途与用法。见: 原表
-- rewrite function class, support inheritance
local class = function (class_name, base_class)
if type(class_name) ~= "string" then
error("class name must be string")
end
local cls = {__class_name = class_name}
-- 新增代码,以上部分代码与上文一致
if base_class then
cls.__super = base_class
-- 将 metatable 的 __index 方法指向父类
-- 天然地实现了继承
-- 当数据/方法不存在于子类时,检索父类
setmetatable(cls, {__index = cls.__super})
else
setmetatable(cls, {__index = cls})
end
-- 以下部分与上文一致
cls.ctor = function ( ... )
print("default ctor function")
end
cls.new = function ( ... )
local o = {}
setmetatable(o, {__index = cls})
o.__class = cls
o.ctor(...)
return o
end
return cls
end
function main()
-- Base Class
local animal = class("animal")
function animal:ctor( ... )
print("constructor an animal ", ...)
self.name = ...
end
function animal:run( ... )
print(string.format("%s is running.", self.name))
end
-- Child Class
local turtle = class("turtle", animal)
function turtle:ctor( ... )
self.__super:ctor(...)
print("constructor a turtle ", ...)
self.name = ...
end
function turtle:swim( ... )
print(string.format("%s is swimming.", self.name))
end
-- rewrite run method
function turtle:run( ... )
print(string.format("A turtle %s is running.", self.name))
end
local turtle_1 = turtle:new("turtle_1")
local animal_1 = animal:new("animal_1")
turtle_1:swim()
local function run_animal(a)
a:run()
end
run_animal(turtle_1)
run_animal(animal_1)
end
main()
-- output
constructor an animal turtle_1
constructor a turtle turtle_1
constructor an animal animal_1
turtle_1 is swimming.
A turtle turtle_1 is running.
animal_1 is running.
注意输出的第五行,不是 turtle_1 is running.
而是 A turtle turtle_1 is running.
这里我们可以发现父类(animal)的 run
方法已经被子类(turtle)的 run
方法覆盖,或称重写。(注意与重载区分)
即,我们实现了多态。
继承类 - 多重继承
在上一部分,我们已经实现了一个可以支持继承与多态的类。其实至此,面向对象的三大特性已经全部实现了。
在本部分,我们修改 metatable
的 __index
的逻辑,使得我们的类支持多重继承。
-- rewrite function class, support multiple inheritance
local class = function (class_name, ...)
if type(class_name) ~= "string" then
error("class name must be string")
end
local cls = {__class_name = class_name}
-- 新增代码,以上部分代码与上文一致
local base_classes = {...}
cls.__supers = {}
local cnt = 1
-- 将传入的基类全部存至 cls.__supers 中
for _, base_class in ipairs(base_classes) do
cls.__supers[cnt] = base_class
cnt = cnt + 1
end
if #cls.__supers > 0 then
setmetatable(cls, {
__index = function(_, key)
-- 轮训所有父类是否含有该属性/方法
-- 有即立刻返回
for _, super in ipairs(cls.__supers) do
if super[key] then
return super[key]
end
end
end
})
end
-- 以下部分与上文一致
cls.ctor = function ( ... )
print("default ctor function")
end
cls.new = function ( ... )
local o = {}
setmetatable(o, {__index = cls})
o.__class = cls
o.ctor(...)
return o
end
return cls
end
可见,与继承类 - 继承与多态中的 class
相比,此时的 class
再次修改了 metatable 的 __index
逻辑。当某属性/方法不存在于子类中时,将依次轮训父类中有无该属性/方法。基于此逻辑,实现了多重继承。这样的多重继承,也存在一个问题。就是当不同父类都具有相同属性/方法时,实际的继承结果就与父类的书写顺序有关。
function main()
-- class boy ################################################################
local boy = class("boy")
function boy:ctor( ... )
print("constructor a boy ", ...)
self.sexual = "male"
end
function boy:say()
print(string.format("%s is a boy", self.name))
end
-- end class boy ############################################################
-- class student ############################################################
local student = class("student")
function student:ctor( ... )
self.number = 1
self.student_name = "student_" .. ...
print("constructor a student ", self.student_name)
end
function student:say()
print(string.format("%s is a student", self.name))
end
function student:study()
print(string.format("No.%s %s is doing homework", self.number, self.name))
end
-- end class student ########################################################
-- class bad guy ############################################################
local bad_guy = class("bad_guy", boy, student)
function bad_guy:ctor( ... )
for _, super in ipairs(self.__supers) do
super:ctor(...)
end
print("constructor a bad guy ", ...)
self.name = ...
end
function bad_guy:do_bad_thing()
print(string.format("No.%s %s is doing bad things", self.number, self.name))
end
-- end class bad guy ########################################################
local bg_1 = bad_guy:new("Tom")
bg_1:say()
bg_1:study()
bg_1:do_bad_thing()
end
main()
-- output
constructor a boy Tom
constructor a student student_Tom
constructor a bad guy Tom
Tom is a boy
No.1 Tom is doing homework
No.1 Tom is doing bad things
注意输出的第四行 Tom is a boy
,此时并非输出 Tom is a student
,即多重继承的父类书写次序问题,此时的子类继承了第一个传入的父类 boy
的 say
方法。