[Lua] 实现所有类的基类Object、模拟单继承OO、实现抽象工厂

这篇具有很好参考价值的文章主要介绍了[Lua] 实现所有类的基类Object、模拟单继承OO、实现抽象工厂。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

所有类的基类 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 cls
end

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
  end
end

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 false
end

__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 obj
end

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

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

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


实验-抽象工厂

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

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

我们先定义敌人抽象类

Enemy = Object:extend()

Enemy.draw = unrealized
Enemy.new = function(self)
  self.hp = 100
end

然后定义继承抽象类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 = w
end

SquareEnemyWhite = SquareEnemy:extend()
SquareEnemyWhite.draw = function(self)
  love.graphics.setColor(1, 1, 1)
  love.graphics.rectangle("fill", self.x, self.y, self.w, self.w)
end

SquareEnemyRed = SquareEnemy:extend()
SquareEnemyRed.new = function(self, ...)
  SquareEnemyRed.super.new(self, ...)
  self.hp = 200
end
SquareEnemyRed.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 = unrealized
IFactory.squareEnemy = unrealized
IFactory.barEnemy = unrealized

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

WhiteFactory = Object:extend()
WhiteFactory:implement(IFactory)

WhiteFactory.circleEnemy = function(...)
  return CircleEnemyWhite(...)
end

WhiteFactory.squareEnemy = function(...)
  return SquareEnemyWhite(...)
end

WhiteFactory.barEnemy = function(...)
  return BarEnemyWhite(...)
end

RedFactory = Object:extend()
RedFactory:implement(IFactory)

RedFactory.circleEnemy = function(...)
  return CircleEnemyRed(...)
end

RedFactory.squareEnemy = function(...)
  return SquareEnemyRed(...)
end

RedFactory.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)
  end
end

love.draw = function()
  for _, enemy in ipairs(enemies) do
    enemy:draw()
  end
end

参考资料

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

其它

oo.lua

Object = {}
Object.__index = Object

function Object:new()
end

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 cls
end

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
  end
end

function Object:is(T)
  local mt = getmetatable(self)
  while mt do
    if mt == T then
      return true
    end
    mt = getmetatable(mt)
  end
  return false
end

function Object:__tostring()
  return "Object"
end

function Object:__call(...)
  local obj = setmetatable({}, self)
  obj:new(...)
  return obj
end

function 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 ?
]]

点我返回文章来源地址https://www.toymoban.com/news/detail-671329.html


到了这里,关于[Lua] 实现所有类的基类Object、模拟单继承OO、实现抽象工厂的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++的基类和派生类构造函数

    基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承。构造函数不能被继承是有道理的,因为即使继承了,它的名字和派生类的名字也不一样,不能成为派生类的构造函数,当然更不能成为普通的成员函数。

    2024年02月10日
    浏览(41)
  • 如何用IDEA查看一个类的所有继承关系

    最近在研究集合的继承关系图,在网上查的都不是很全,所以就想能不能直接看jar包里面的以来关系,在网上查了一下方法,总结如下: 工具:idea 功能:diagram 在你想查看的类内,点击右键,选择 Diagrams,其中有 show Diagrams和 show Diagrams Popup,前者新建在标签页内,后者以浮

    2024年02月04日
    浏览(32)
  • Vue3实现组件级基类的几种方法

    Vue3的组件有三种代码组织方式 纯Option API (不含setup) option API + setup 纯 setup (即composition API) 对于这三种形式,设置基类的方法也略有不同。 vue3提供了 mixins和extends,但是尝试之后发现这两种方法只支持纯OptionAPI,设置的data会被识别,但是设置的setup里return 的 reactive,完

    2024年02月01日
    浏览(47)
  • 作为所有类的顶层父类,没想到Object的魔力如此之大!

    在上一篇博文中我们提到了Java面向对象的四大特性,其中谈及“抽象”特性时做了一个引子,引出今天的主人公Object,作为所有类的顶级父类,Object被视为是James.Gosling的哲学思考,它高度概括了事务的自然与社会行为。 跟进Object类的源码中我们可以看到,类的注释中对它做

    2024年02月01日
    浏览(30)
  • Unity UGUI的Outline(描边)组件的介绍及ABP框架提供的增删改查的基类使用

    Outline(描边)组件是Unity UGUI中的一种特效组件,用于给UI元素添加描边效果。通过设置描边的颜色、宽度和模糊程度,可以使UI元素在视觉上更加突出。 Outline(描边)组件通过在UI元素周围绘制多个相同的UI元素,并设置不同的颜色和大小,从而实现描边的效果。描边的宽度和模糊

    2024年02月21日
    浏览(31)
  • C++ | 继承(基类,父类,超类),(派生类,子类)

    文章参考:https://blog.csdn.net/war1111886/article/details/8609957 一 .继承中的访问权限关系 1.基类,父类,超类是指被继承的类,派生类,子类是指继承于基类的类.  2.在C++中使用: 冒号表示继承,如class A : public B;表示派生类A从基类B继承而来 3.派生类包含基类的所有

    2024年02月15日
    浏览(30)
  • 基类与派生类对象的关系 派生类的构造函数

    🐶博主主页: @ᰔᩚ. 一怀明月ꦿ  ❤️‍🔥 专栏系列: 线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++ 🔥 座右铭: “不要等到什么都没有了,才下定决心去做” 🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀 目录 🐰

    2023年04月10日
    浏览(31)
  • effective c++ 43-处理模板化基类的名称 笔记

    该节主要分析了一个写模板时常常会遇到的一个编译错误。 这里有一个模板基类,有派生类继承了模板基类,并调用了基类中的方法,但是编译器却会报找不该方法,这是怎么回事? 编译输出如下: 从编译的输出也可以看出,原因是编译器觉得 sendClear 含义不明确,编译器

    2024年02月03日
    浏览(42)
  • 【Unity之IMGUI】—位置信息类和控件基类的封装

    👨‍💻个人主页 :@元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏 : 缺点1:无法在编译过程进行可视化调整 缺点2:无法分辨率自适应 作用:让控件根据调整对齐 最终代码 特点: 类是抽象类:原因基类

    2024年02月12日
    浏览(25)
  • 基于C/C++将派生类赋值给基类的详细讲解

    目录 将派生类对象赋值给基类对象 将派生类指针赋值给基类指针

    2024年02月08日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包