所有类的基类 Object
Lua 没有严格的 oo(Object-Oriented)定义,可以利用元表特性来实现
先定义所有类的基类,即Object类。代码顺序从上到下,自成一体。完整代码
定义一个空表 Object ,__index 指向其自身(继承将直接使用该表作为对象的元表)- Object = {}
- Object.__index = Object
复制代码 new 定义构造对象时的初始化行为,相当于构造器。基类不需要进行任何初始化操作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 用于对象 print 或 tostring 时自定义字符串化- 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,在需要的地方导入模块,使用 Object 和 unrealized 这两个全局变量
实验-抽象工厂
接下来实现抽象工厂模式。抽象工厂能创建一系列相关的对象,而无需指定其具体类。
考虑如下情况,有多类敌人(正方形、圆形、长条),敌人初始化是两种状态的一种(正常状态,厚血状态),且后期敌人和状态种类还会增多
我们先定义敌人抽象类- Enemy = Object:extend()
- Enemy.draw = unrealized
- Enemy.new = function(self)
- self.hp = 100
- end
复制代码 然后定义继承抽象类Enemy的抽象类SquareEnemy,与继承抽象类SquareEnemy的两个普通类SquareEnemyWhite、SquareEnemyRed。圆形敌人跟长条敌人同理。- 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 = 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 cls
- endfunction 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
- endfunction Object:is(T)
- local mt = getmetatable(self)
- while mt do
- if mt == T then
- return true
- end
- mt = getmetatable(mt)
- end
- return false
- endfunction Object:__tostring()
- return "Object"
- endfunction Object:__call(...)
- local obj = setmetatable({}, self)
- obj:new(...)
- return obj
- endfunction 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 ?
- ]]
复制代码 点我返回
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |