所有类的基类 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 ?
]]

点我返回

[Lua] 实现所有类的基类Object、模拟单继承OO、实现抽象工厂的更多相关文章

  1. Java如何解决脆弱基类(基类被冻结)问题

    概述  大多数好的设计者象躲避瘟疫一样来避免使用实现继承(extends 关系).实际上80%的代码应该完全用interfaces写,而不是通过extends.“JAVA设计模式”一书详细阐述了怎样用 ...

  2. 从零开始学C++之继承(二):继承与构造函数、派生类到基类的转换

    一.不能自动继承的成员函数 构造函数 析构函数 =运算符 二.继承与构造函数 基类的构造函数不被继承,派生类中需要声明自己的构造函数. 声明构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类 ...

  3. C++ 派生类到基类转换的可访问性

    今天看c++ primer关于派生类到基类转换的可访问性,看的很晕,看了下面的文章恍然大悟: http://www.2cto.com/kf/201403/283389.html C++ primer第 ...

  4. c++ primer 学习杂记2【派生类到基类转换的可访问性】

    参考: http://blog.csdn.net/rehongchen/article/details/7930853 http://blog.csdn.net/ming_road/article/d ...

  5. 转 关于C#中派生类调用基类构造函数的理解

    关于C#中派生类调用基类构造函数的理解 .c#class       本文中的默认构造函数是指在没有编写构造函数的情况下系统默认的无参构造函数 1.  当基类中没有自己编写构造函数时,派生类默认的调用 ...

  6. c++ 派生类向基类转换的可访问性

    对于c++面向对象一直很疑惑,这次决定下功夫把它弄明白 一.派生类和基类之间的类型转换 首先理解,派生类含有基类的所有成分,只不过有些就算在派生类的成员函数也不能访问而已. (1)派生类和基类的自动转 ...

  7. c++中派生类对基类成员的三种访问规则(转)

    C++中派生类对基类成员的访问形式主要有以下两种:1.内部访问:由派生类中新增成员对基类继承来的成员的访问.2.对象访问:在派生类外部,通过派生类的对象对从基类继承来的成员的访问.今天给大家介绍在3中 ...

  8. c++——派生类和基类转换(类型兼容性原则)

    基类也叫父类,派生类也叫子类. 类之间的继承关系继承关系是类之间的父子关系. 继承关系的特点如下:A. 子类拥有父类的所有属性和行为B. 子类也是一种特殊的父类C. 子类对象可以当父类对象使用D. 子 ...

  9. C++_派生类的构造函数及派生类和基类之间的特殊关系

    派生类和基类的概念及派生类构造函数的原理: 创建一个叫做TableTennisPlayer的基类,记录会员的名字和是否有球桌. //声明一个基类 class TableTennisPlayer { p ...

  10. C# 派生和继承(派生类与基类)

    using System; using System.Collections.Generic; using System.Text; namespace 继承 { class Program { st ...

随机推荐

  1. ClickHouse笔记: Ubuntu/Centos下的安装, 配置和用户管理

    ClickHouse ClickHouse 属于 OLAP 数据库 OLTP 与 OLAP OLTP (On-Line Transaction Processing 联机事务处理), 注重事务处理, ...

  2. Ubuntu 18.04 (Bionic) 简单快速的安装mongodb

    按步骤走,不带脑子式安装(注意4.0版本mongodb官方已经不再支持,以下代码中可以修改mongodb版本号安装,目前最新版为6.0,如果懒得改直接用也可以,文章后边第三章第一条代码会直接升级为最新 ...

  3. 【最佳实践】如何设计 RESTful API ?

    良好的 API 设计是一个经常被提及的话题,特别是对于那些试图完善其 API 策略的团队来说.一个设计良好的 API 的好处包括:改进开发者体验.更快速地编写文档以及更高效地推广你的 API.但是,到 ...

  4. 一文吃透Java并发高频面试题

    内容摘自我的学习网站:topjavaer.cn 分享50道Java并发高频面试题. 线程池 线程池:一个管理线程的池子. 为什么平时都是使用线程池创建线程,直接new一个线程不好吗? 嗯,手动创建线程 ...

  5. Galaxy Project | 生信人最值得学习的开源项目之一

    我与 Galaxy Project 的渊源可以追溯到我刚毕业,还在华大实习的那一段时间,这个项目应该是我职业生涯中最重要的一段经历.虽然这么对年以来一直都关注着这个项目,但大多数都是浅尝辄止,对源码层 ...

  6. CANoe工具的安装

    CANoe是德国Vector公司为汽车总线的开发而设计的一款总线开发环境,全称叫CAN open environment,用于分析和模拟CAN(Controller Area Network)和LIN ...

  7. 揭秘Spring依赖注入和SpEL表达式

    摘要:在本文中,我们深入探讨了Spring框架中的属性注入技术,包括setter注入.构造器注入.注解式属性注入,以及使用SpEL表达式进行属性注入. 本文分享自华为云社区<Spring高手之路 ...

  8. 【保姆级教学】抓包工具Wireshark使用教程

    wireshark介绍 今天讲一下另一款底层抓包软件,之前写过两篇抓包软件 分别是 fiddler抓包[https://www.cnblogs.com/zichliang/p/16067941.htm ...

  9. 国标GB28181协议客户端开发(二)程序架构和注册

    国标GB28181协议客户端开发(二)程序架构和注册 本系列文章旨在探讨国标GB28181协议设备端的开发过程.本文将聚焦于架构设计和设备注册,并详细介绍了设备端的程序架构设计.exosip库介绍和接 ...

  10. Python运维开发之路《文件处理》

    一.python字典操作 1.字典的定义 字典一种key-value的数据类型,类似于json串. 2.字典的特性 key:value 字典不可变,key唯一 key的定义规则: 1.不可变,数字.字 ...