首页 > 其他分享 >简单实现Lua的面向对象

简单实现Lua的面向对象

时间:2024-05-26 22:05:32浏览次数:16  
标签:__ end .. 面向对象 Object Lua init 简单 Test

参考了多篇文章和以往的项目经验,这里来写一篇在Unity游戏客户端开发面试的时候经常会被问到的如何实现lua的面向对象功能。

众所周知面向对象有三大特性,封装、继承、多态,那么在这里我们用lua来分别实现这三个功能。

一、封装

假设我有一个类,我想要快速创建一个该类的实例化对象,那么我肯定是希望有一个通用的函数来创建,例如new之类的;另外由于lua底层是没有面向对象这种东西的,那么我肯定不希望每次要用一次对象就重新写一套。那么需求就很明确了:1、封装一个基类对象,如Object,此后实现别的对象都可以从此对象中继承(后续完成继承功能);2、完成一系列的基本的对象相关的函数,如new——用于实例化,init——用于初始化,inherit——用于继承,super——用于访问父类等等。

此处假设读者们都有lua基础,其lua基础函数功能可以自行查找其他资料了解(主要是本人也讲不清楚)。在上述的需求下,我们首先设置一个基础的表,叫做Object:

Object = {}
Object.__className = "Object"
function Object:New(...)
	local o = {}
	setmetatable(o, {__index = self})
	o:__init__(...)
	return o
end

function Object:__init__()
	print("this is "..self.__className.." init")
end

function Object:Test()
	print("this is "..self.__className.." Test")
end

local objBase = Object:New()
objBase:Test()

在这里我们的New函数创建了一个空表o,并将其__index与Object链接起来,设置为o的元表,这样o就可以通过键名去访问Object中的对应元素,这里就实现了基本的对象的封装,通过New可以快速创建Object的实例化对象。

二、继承

在实现继承的时候分析如下需求:1、子类需要继承父类的全部变量;2、子类可自由添加新的变量;3、子类需要可以访问父类;4、父类调用子类(子类重写父类同名函数)函数/变量;

当子类需要继承父类中全部变量的时候,可以有诸如

setmetatable(o, {__index = Base})

这样的写法,但是总而言之不够简洁,因为在创建子类的时候我需要在子类中声明

objectChild = {}
function objectChild:New()
    setmetatable(objectChild, {__index = base})--base为父类
end

每次创建子类继承对象的时候就要设置一次元表。那么有什么方法可以更加快速地创建继承的对象呢?答案是在基类中设置一个继承函数Inherit,通过这个函数将父类中的元素都复制到子类的表中,包括new方法:

function Object:Inherit(o)
	local o = o or {}
	
	for k, v in pairs(self) do
		assert(not o[k])
		if not o[k] then
			o[k] = v
		end
	end

	o.__base = self
	return o
end

其中设置base为自身,也为子类提供了访问父类的方法。当我创建一个子类的时候,只需要:

ObjChild = Object:Inherit()
ObjChild.__className = "ObjChild"

function ObjChild:__init__()
	--Super(ObjChild):__init__(self)
	print("this is "..self.__className.." init")
end
function ObjChild:Test()
	print("this is "..self.__className.." Test")
end

调用一次父类中的Inherit函数即可,他会将父类表中所有元素复制到子表中。那么我在实例化子类对象的时候,会调用父类中的New函数,New函数又将创建的实例对象的__index链接到类中来,从而访问到子类中(包括父类)的所有变量。

local objChild = ObjChild:New()
objChild:Test()

        多层继承

关于多层继承,由于上述的Inherit为逐级复制,例如child1继承Object,child2继承child1,那么在child2调用child1:Inherit()方法的时候,实际上child1也一定是由Object:Inherit()得来的,因此child2也会完全的继承Object中的全部元素。

ObjChild2 = ObjChild:Inherit()
ObjChild2.__className = "ObjChild2"

function ObjChild2:__init__()
	Super(ObjChild2):__init__(self)
	print("this is "..self.__className.." init")
end
function ObjChild2:Test()
	print("this is "..self.__className.." Test")
end

local objChild2 = ObjChild2:New()
objChild2:Test()

三、多态

在考虑多态的功能的时候,首先考虑到子类对父类同名函数的重写,这时候重写需要考虑是否调用父类的同名函数。在这里我们首先实现一下访问父类函数的 通用方法:

function Super(obj)
	return obj.__base
end

这里的__base是我们在New的时候将其指向父类的一个变量。这时候可能有的同学可能会问:欸,那我直接通过__base:xxxx()访问不就可以了吗,为什么要多此一举?首先在lua中没有访问权限控制的功能,因此我们在做代码规范的时候会规定诸如下划线开头的变量和方法被认为是私有变量,不能随意访问;大写开头的变量和方法被认为是公有变量,可以随意访问修改等等。

在上述的代码中,会发现我在每个继承的子类中重写了Test函数,在访问实例化的子类对象的时候,调用其Test函数,有的设置了访问父类同名函数,有的没设置,这样就能达到多态的目的。

完整代码如下:

Object = {}
Object.__className = "Object"
function Object:New(...)
	local o = {}
	setmetatable(o, {__index = self})
	o:__init__(...)
	return o
end
function Object:Inherit(o)
	local o = o or {}
	
	for k, v in pairs(self) do
		assert(not o[k])
		if not o[k] then
			o[k] = v
		end
	end

	o.__base = self
	return o
end

function Object:__init__()
	print("this is "..self.__className.." init")
end

function Object:Test()
	print("this is "..self.__className.." Test")
end

function Super(obj)
	return obj.__base
end

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

local objBase = Object:New()
objBase:Test()

ObjChild = Object:Inherit()
ObjChild.__className = "ObjChild"

function ObjChild:__init__()
	--Super(ObjChild):__init__(self)
	print("this is "..self.__className.." init")
end
function ObjChild:Test()
	print("this is "..self.__className.." Test")
end

local objChild = ObjChild:New()
objChild:Test()

ObjChild2 = ObjChild:Inherit()
ObjChild2.__className = "ObjChild2"

function ObjChild2:__init__()
	Super(ObjChild2):__init__(self)
	print("this is "..self.__className.." init")
end
function ObjChild2:Test()
	print("this is "..self.__className.." Test")
end

local objChild2 = ObjChild2:New()
objChild2:Test()

运行结果如下:

this is Object init
this is Object Test
this is ObjChild init
this is ObjChild Test
this is ObjChild init
this is ObjChild2 init
this is ObjChild2 Test

这样我们就简单地实现了lua的面向对象功能了。有其他的问题和补充欢迎在评论区提出。

标签:__,end,..,面向对象,Object,Lua,init,简单,Test
From: https://blog.csdn.net/weixin_43773483/article/details/139220684

相关文章

  • 创建HTML简单页面
    一.<!--在vscode中用html语言编写一个html页面网站-->   <!--创建的页面主题为:端午那些事-->1.代码如下:2.上面的代码显示出的页面如下:3.因为不能发视频,所以粗糙地截了下图。解释:上面的带下划线的字体是可以跳转页面的,开始的五行是可以在通过锚点来跳转......
  • 20240521考试(1.打印数字字符 2.打印面积(类的继承)3.简单的矩阵加减)
    目录1.打印数字字符2.打印面积(类的继承)3.简单的矩阵加减1.打印数字字符#include<iostream>#include<iomanip>usingnamespacestd;classdatatype{public:datatype(){}datatype(charc):c(c),i(0),f(0){}datatype(inti):c(0),i......
  • 简单版基本电路与电子学实验备考(有待补充)
    基本电路与电子学实验备考戴维南定理Step1测等效电阻12元件箱12V电压源(实验台最右侧)与电压表相接测实际输出保证电压源1210mA的电流源(粗调转钮量程20)测电流电压时注意量程短路电流(开关打向左侧)开路电压(右侧两位小数)据此算出等效电阻R0记录数据UocIscR0Step2构建......
  • SpringBoot+MySQL的简单运用(Hello World API)
    一、创建springboot项目1.生成springboot首先打开自动生成springboot项目的在线网站:https://start.spring.io/如下图所示:2.依赖配置在上述页面中,左边默认就好,不用操作,右边添加依赖,点击下图中所示红色区域在弹出的栏目中依次搜索SpringWeb,SpringDataJPA,MySQLDr......
  • 【Java笔记】第8章:面向对象的三大特性(封装、继承、多态)
    前言1.三大特性概述2.封装3.继承4.多态结语#include<GUIQU.h>intmain{上期回顾:【Java笔记】第7章:面向对象个人主页:C_GUIQU归属专栏:【Java学习】return一键三连;}前言各位小伙伴大家好!上期小编给大家讲解了Java中的面向对象,接下来讲讲Java中面向......
  • 震惊 vue+express创建的node上线竟然如此简单!
    1去阿里云申请服务器   首先登录阿里云,搜索服务器esc选择免费试用选择centos选择一台服务器   然后进入服务器控制台,点击概况选择重置密码修改root用户密码用户名为root不建议修改   点击远程连接输入自己的密码   输入命令安装宝塔面板 ......
  • aardio 简单工厂模式
    1//calculate简单工厂模式23//简单工厂模式4importconsole;56//运算类7classoperation{8ctor(){};9numberA=0;10numberB=0;11getResult=function(){12varresult=0;13returnresult;14}1......
  • lua 元表__index 元方法应用1
    1--普通表2localtab={1,2,3}34--元表5localmeta={6insert=function(t,v)7--监测表增加成员8print("增加一个值",v)9table.insert(t,v)10end,11remove=function(t,i)12--监测表删除成......
  • dremio nginx proxy 问题简单说明二
    以前说过基于修改dremio服务的静态index.html实际上还有一种方法就是直接通过nginx的sub_filter指令,以下是简单说明配置核心是nginx,后端服务就不需要修改了nginx.conf核心是sub_filter的匹配处理worker_processes1;events{worker_connections......
  • 超简单白话文机器学习 - 回归树&树剪枝(含算法介绍,公式,源代码实现以及调包实现)
    1.回归树1.1算法介绍大家看到这篇文章时想必已经对树这个概念已经有基础了,如果不是很了解的朋友可以看看笔者的这篇文章:超简单白话文机器学习-决策树算法全解(含算法介绍,公式,源代码实现以及调包实现)_白话决策树-CSDN博客对于回归树的建立,我们一般使用CART回归树,CART(Clas......