加入收藏

[Lua] 如何模拟单继承OO、实现抽象工厂

2023-08-27 14:37:07 来源:博客园

所有类的基类 Object

Lua 没有严格的 oo(Object-Oriented)定义,可以利用元表特性来实现

先定义所有类的基类,即Object类。代码顺序从上到下,自成一体。完整代码

定义一个空表 Object__index指向其自身(继承将直接使用该表作为对象的元表)


(相关资料图)

Object = {}Object.__index = Object

new定义构造对象时的初始化行为,相当于构造器。基类不需要进行任何初始化操作

function Object:new()end

extend实现了类继承,具体流程

  • 创建一个空表 cls,作为类
  • 我们将父类的元方法全部复制给子类 ⭐为什么
  • 子类的 __index指向其自身(子类可被继承)(覆盖了父类复制给子类的 __index
  • 子类的 super字段指向父类
  • 子类的元表指向父类(子类)
function Object:extend()  local cls = {}  for k, v in pairs(self) do    if k:find("__") == 1 then      cls[k] = v    end  end  cls.__index = cls  cls.super = self  setmetatable(cls, self)  return clsend

implement用于实现接口类,可传入多个接口

  • 遍历每个接口 cls
  • 当前对象如果没有实现接口类的某个方法,则将该方法的实现从接口类复制给对象
function Object:implement(...)  for _, cls in pairs({ ... }) do    for k, v in pairs(cls) do      if self[k] == nil and type(v) == "function" then        self[k] = v      end    end  endend

is用于判断某个类或对象实例是否是另一个类

  • 循环拿元表,直到没有为止,最后一个元表一定是 Object
function Object:is(T)  local mt = getmetatable(self)  while mt do    if mt == T then      return true    end    mt = getmetatable(mt)  end  return falseend

__tostring用于对象 printtostring时自定义字符串化

function Object:__tostring()  return "Object"end

直接用类名称,来实现一个对象的实例化。__call可以把变量当函数使用,比如Car类(变量),local mycar = Car(),生成了一个对象实例myCar,属于类Car

  • 创建一个对象(空表),并把自身(类)作为对象的元表
  • 执行构造器,由于对象是空表找不到,所以通过元表的__index也就是去父类找
  • 返回初始化好的对象实例
function Object:__call(...)  local obj = setmetatable({}, self)  obj:new(...)  return objend

全局函数 unrealized用于模拟接口或抽象类未定义的方法,子类未实现时会寄

function unrealized(...)  error("未实现", 2)end

到现在为止已经模拟了一个单继承OO,在需要的地方导入模块,使用 Objectunrealized这两个全局变量

实验-抽象工厂

接下来实现抽象工厂模式。抽象工厂能创建一系列相关的对象,而无需指定其具体类。

考虑如下情况,有多类敌人(正方形、圆形、长条),敌人初始化是两种状态的一种(正常状态,厚血状态),且后期敌人和状态种类还会增多

我们先定义敌人抽象类

Enemy = Object:extend()Enemy.draw = unrealizedEnemy.new = function(self)  self.hp = 100end

然后定义继承抽象类Enemy的抽象类SquareEnemy,与继承抽象类SquareEnemy的两个普通类SquareEnemyWhiteSquareEnemyRed。圆形敌人跟长条敌人同理。

SquareEnemy = Enemy:extend()SquareEnemy.new = function(self, x, y, w)  SquareEnemy.super.new(self)  self.x = x  self.y = y  self.w = wendSquareEnemyWhite = SquareEnemy:extend()SquareEnemyWhite.draw = function(self)  love.graphics.setColor(1, 1, 1)  love.graphics.rectangle("fill", self.x, self.y, self.w, self.w)endSquareEnemyRed = SquareEnemy:extend()SquareEnemyRed.new = function(self, ...)  SquareEnemyRed.super.new(self, ...)  self.hp = 200endSquareEnemyRed.draw = function(self)  love.graphics.setColor(1, 0, 0)  love.graphics.rectangle("fill", self.x, self.y, self.w, self.w)end

定义工厂接口,在这里接口算是一种特殊的抽象类(由于只能用表来模拟接口,所以让接口也继承Objcet)

IFactory = Object:extend()IFactory.circleEnemy = unrealizedIFactory.squareEnemy = unrealizedIFactory.barEnemy = unrealized

分别实现白色工厂和红色工厂(如果没有额外的创建操作,可以不用return)

WhiteFactory = Object:extend()WhiteFactory:implement(IFactory)WhiteFactory.circleEnemy = function(...)  return CircleEnemyWhite(...)endWhiteFactory.squareEnemy = function(...)  return SquareEnemyWhite(...)endWhiteFactory.barEnemy = function(...)  return BarEnemyWhite(...)endRedFactory = Object:extend()RedFactory:implement(IFactory)RedFactory.circleEnemy = function(...)  return CircleEnemyRed(...)endRedFactory.squareEnemy = function(...)  return SquareEnemyRed(...)endRedFactory.barEnemy = function(...)  return BarEnemyRed(...)end

接下来测试抽象工厂

require "oo"require "enemy.aac"require "enemy.bar"require "enemy.circle"require "enemy.square"require "factory.aac"require "factory.red_factory"require "factory.white_factory"enemies = {}love.load = function()  IFactory = WhiteFactory()  table.insert(enemies, IFactory.circleEnemy(100, 100, 25))  table.insert(enemies, IFactory.squareEnemy(100, 200, 25))  table.insert(enemies, IFactory.barEnemy(100, 300, 10, 50))  IFactory = RedFactory()  table.insert(enemies, IFactory.circleEnemy(200, 100, 25))  table.insert(enemies, IFactory.squareEnemy(200, 200, 25))  table.insert(enemies, IFactory.barEnemy(200, 300, 10, 50))  for _, enemy in pairs(enemies) do    print(enemy.hp)  endendlove.draw = function()  for _, enemy in ipairs(enemies) do    enemy:draw()  endend

参考资料

  • 《Lua程序设计·第四版》罗伯托·耶鲁萨林斯希 、第227~241页

其它

oo.lua

Object = {}Object.__index = Objectfunction Object:new()endfunction Object:extend()  local cls = {}  for k, v in pairs(self) do    if k:find("__") == 1 then      cls[k] = v    end  end  cls.__index = cls  cls.super = self  setmetatable(cls, self)  return clsendfunction Object:implement(...)  for _, cls in pairs({ ... }) do    for k, v in pairs(cls) do      if self[k] == nil and type(v) == "function" then        self[k] = v      end    end  endendfunction Object:is(T)  local mt = getmetatable(self)  while mt do    if mt == T then      return true    end    mt = getmetatable(mt)  end  return falseendfunction Object:__tostring()  return "Object"endfunction Object:__call(...)  local obj = setmetatable({}, self)  obj:new(...)  return objendfunction unrealized(...)  error("未实现", 3)end-- return Object

QUESTION1

如果不复制元方法,假设类B继承类A,类B的对象实例b,b的元表是类B,在调用 b + b 时,涉及到算术运算符相关的元方法,b会在父类B中查找__add,找不到并不会顺着B的元表__index再去B的父类A找,因此会报错

A = {  __index = A,  __add = function(a, b)    return a.age + b.age  end,  name = "小白"}B = { __index = B, }b = { __index = b, age = 1 }setmetatable(B, A)setmetatable(b, B)print(b.name)print(b + b)--[[> dofile "TEST/test.lua"小白TEST/test.lua:15: attempt to perform arithmetic on a table value (global "b")stack traceback:        TEST/test.lua:15: in main chunk        [C]: in function "dofile"        stdin:1: in main chunk        [C]: in ?]]

点我返回

关键词:

相关新闻

资讯

首届全国工业互联网创新大赛颁奖仪式在合肥举行
首届全国工业互联网创新大赛颁奖仪式在合肥举行

中国商务新闻网是商务部国际商报社主办,国家互联网信......更多>

之一是什么意思(之是什么意思)
之一是什么意思(之是什么意思)

之一是什么意思,之是什么意思这个很多人还不知道,现......更多>

延吉:以铸牢中华民族共同体意识为主线,让各民族群众深度融合!
延吉:以铸牢中华民族共同体意识为主线,让各民族群众深度融合!

日前,记者在延吉建工街道延虹社区办公楼看到,在二楼......更多>

日本启动福岛核事故污染水排海  国家原子能机构发声
日本启动福岛核事故污染水排海 国家原子能机构发声

8月24日,日本政府无视国际社会强烈质疑和反对,单方......更多>

纯碱最近大幅上涨!策略上该如何应对?
纯碱最近大幅上涨!策略上该如何应对?

银河期货首席策略师沈恩贤投资咨询证号:Z0013972纯碱......更多>

冬天空调温度多少合适制热(冬天空调温度多少合适)
冬天空调温度多少合适制热(冬天空调温度多少合适)

1、冬季将空调温度设定在20度的最佳温度,室内温度适......更多>

孙广阔(关于孙广阔简述)
孙广阔(关于孙广阔简述)

,你们好,今天0471房产来聊聊一篇广阔,广阔简述的文......更多>

洪兴股份:计提减值准备合计1961.27万元
洪兴股份:计提减值准备合计1961.27万元

洪兴股份:计提减值准备合计1961 27万元...更多>

第四批鼓励研发申报儿童药品清单发布
第四批鼓励研发申报儿童药品清单发布

《第四批鼓励研发申报儿童药品清单》(以下简称《第四......更多>

中甲综述:四川九牛客场赢球继续领跑 广西平果哈嘹1-0力克广州队
中甲综述:四川九牛客场赢球继续领跑 广西平果哈嘹1-0力克广州队

直播吧8月26日讯今天进行了中甲第20轮5场比赛,四川九......更多>

关注

机器人:目前公司核应急机器人处于示范应用阶段
机器人:目前公司核应急机器人处于示范应用阶段
机器人在投资者互动平台表示,公司核应急机器人可应用... 更多>
机器人:目前公司核应急机器人处于示范应用阶段
机器人在投资者互动平台表示,公司核应急机器人可应用... 更多>
法国拟加强管控关键原材料产业,外媒炒作“瞄准中国”
不过接受《环球时报》记者采访的专家认为,法国的相关... 更多>
上海又新增380个早餐网点
补填盲点仍然是早餐民心工程的重头戏。记者昨天在市商... 更多>
陈姓女孩寓意好的名字 陈姓女孩寓意好的名字单字
1、陈子璇2、陈语倩3、陈雪薇4、陈怡君5、陈慧憬6、陈... 更多>
和斯思域柯达明锐pro哪个车好(斯柯达明锐pro与本田思域的区别是什么)
1、明锐斯柯达pro和本田思域的区别在于车身结构不同:... 更多>
感谢语简短 感谢语
1、铭感五内比喻内心非常感激。2、感激涕零感激得掉下... 更多>
凯丰源2023年上半年净利-75.56万 由盈转亏
挖贝网2023年8月25日,凯丰源(873327)近日发布2023... 更多>
港媒:国泰航空首次在内地市场招聘空中乘务员
国泰航空行政总裁林绍波透露,未来将不断扩大内地人才... 更多>
动力电池装机量持续提升 相关上市公司业绩有望受益
多位业内人士向记者表示,“从1月份至7月份的情况来看... 更多>
安徽:“全民反诈在行动”集中宣传活动启动
原标题:“全民反诈在行动”集中宣传活动启动安徽日报... 更多>
湖北省保康县发布雷雨大风黄色预警
保康县气象台2023年08月24日17时55分发布雷雨大风黄色... 更多>
践行绿色可持续 亿万克加速数据中心的现代化之路
在数字经济时代,IT基础架构也在面临重塑。云原生、AI... 更多>