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

这篇具有很好参考价值的文章主要介绍了[Lua] 如何模拟单继承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-671660.html


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

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

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

相关文章

  • 抽象工厂设计模式go实现尝试

    本文章尝试使用go实现“抽象工厂”。 新人设计模式理解,望大家多多指点。

    2024年02月16日
    浏览(52)
  • 用Rust实现23种设计模式之抽象工厂

    在 Rust 中,可以使用 trait 和泛型来实现抽象工厂模式。抽象工厂模式是一种创建型设计模式,它提供了一个接口来创建一系列相关或依赖对象的家族,而无需指定具体的类。下面是一个简单的示例,展示了如何使用 Rust 实现抽象工厂模式: 在上述示例中,我们首先定义了抽

    2024年02月14日
    浏览(61)
  • 【unity之IMGUI实践】抽象父类继承实现【三】

    👨‍💻个人主页 :@元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏 : unityUI专题篇 🅰️ 总效果图 😶‍🌫️:步骤 1.创建游戏面板,进行组件管控 2.创建提示面板进行退出确定 3.同步面板直接的显隐和复

    2024年02月16日
    浏览(49)
  • 【设计模式】Head First 设计模式——抽象工厂模式 C++实现

    设计模式最大的作用就是在变化和稳定中间寻找隔离点,然后分离它们,从而管理变化。将变化像小兔子一样关到笼子里,让它在笼子里随便跳,而不至于跳出来把你整个房间给污染掉。 提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定他们具

    2024年02月10日
    浏览(43)
  • [设计模式Java实现附plantuml源码~创建型] 产品族的创建——抽象工厂模式

    前言: 为什么之前写过Golang 版的设计模式,还在重新写 Java 版? 答:因为对于我而言,当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言,更适合用于学习设计模式。 为什么类图要附上uml 因为很多人学习有做笔记的习惯,如果单纯的只是放一张图片,那

    2024年01月22日
    浏览(65)
  • 设计模式之~工厂系列(简单工厂、工厂方法、抽象工厂)

    目录 简单工厂模式 工厂方法模式 简单工厂 VS 工厂方法 抽象工厂模式: 拓展: 利用简单工厂模式优化抽象工厂   利用反射+抽象工厂   进行优化 反射+配置文件+抽象工厂进行优化 优点:简单工厂模式的最大优点在于工厂类包含了必要的逻辑判断,根据客户端的选择条件动

    2024年02月07日
    浏览(56)
  • 工厂模式和抽象工厂模式的异同

    工厂模式(Factory Pattern)和抽象工厂模式(Abstract Factory Pattern)都是创建型设计模式,用于创建对象。它们有一些相似之处,但也有一些重要的区别。 相同之处: 创建对象:工厂模式和抽象工厂模式都用于创建对象,将对象的实例化过程从客户端代码中解耦。 抽象接口:两

    2024年02月13日
    浏览(43)
  • 设计模式:简单工厂、工厂方法、抽象工厂的区别

    描述: 简单工厂模式并不是严格意义上的设计模式,而更像是一种编程习惯或者说是一种创建对象的简单方式。它使用一个工厂类来创建对象,这个工厂类包含一个方法,根据输入的参数或条件来创建相应的对象实例。 举例: 描述: 工厂方法模式是一种创建型模式,它定义

    2024年01月21日
    浏览(48)
  • 自学设计模式(简单工厂模式、工厂模式、抽象工厂模式)

    使用工厂模式来生产某类对象(代码简化且容易维护,类之间有血缘关系,可以通过工厂类进行生产); 简单工厂模式(用于创建简单对象) 对于简单工厂模式,需要的工厂类只有一个; 在工厂类中的公共成员函数来创建所需对象; 工厂模式 简单工厂模式会违反开放封闭

    2024年02月11日
    浏览(43)
  • 创建型(一) - 简单工厂模式、工厂方法模式和抽象工厂模式

    本文使用了王争老师设计模式课程中的例子,写的很清晰,而且中间穿插了代码优化。 由于设计模式就是解决问题的一种思路,所以每个设计模式会从问题出发,这样比较好理解设计模式出现的意义。 一、简单工厂模式 解决问题:在调用时不想判断来实例化哪一个类或者实

    2024年02月12日
    浏览(40)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包