所有類的基類 Object
Lua 沒有嚴(yán)格的 oo(Object-Oriented)定義,可以利用元表特性來實現(xiàn)
先定義所有類的基類,即Object
類。代碼順序從上到下,自成一體。完整代碼
定義一個空表 Object
,__index
指向其自身(繼承將直接使用該表作為對象的元表)
Object = {}
Object.__index = Object
new
定義構(gòu)造對象時的初始化行為,相當(dāng)于構(gòu)造器?;惒恍枰M行任何初始化操作
function Object:new()
end
extend
實現(xiàn)了類繼承,具體流程
- 創(chuàng)建一個空表
cls
,作為類 - 我們將父類的元方法全部復(fù)制給子類 ?為什么
- 子類的
__index
指向其自身(子類可被繼承)(覆蓋了父類復(fù)制給子類的__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
用于實現(xiàn)接口類,可傳入多個接口
- 遍歷每個接口
cls
- 當(dāng)前對象如果沒有實現(xiàn)接口類的某個方法,則將該方法的實現(xiàn)從接口類復(fù)制給對象
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
用于判斷某個類或?qū)ο髮嵗欠袷橇硪粋€類
- 循環(huán)拿元表,直到?jīng)]有為止,最后一個元表一定是 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
直接用類名稱,來實現(xiàn)一個對象的實例化。__call
可以把變量當(dāng)函數(shù)使用,比如Car
類(變量),local mycar = Car()
,生成了一個對象實例myCar
,屬于類Car
- 創(chuàng)建一個對象(空表),并把自身(類)作為對象的元表
- 執(zhí)行構(gòu)造器,由于對象是空表找不到,所以通過元表的
__index
也就是去父類找 - 返回初始化好的對象實例
function Object:__call(...)
local obj = setmetatable({}, self)
obj:new(...)
return obj
end
全局函數(shù) unrealized
用于模擬接口或抽象類未定義的方法,子類未實現(xiàn)時會寄
function unrealized(...)
error("未實現(xiàn)", 2)
end
到現(xiàn)在為止已經(jīng)模擬了一個單繼承OO,在需要的地方導(dǎo)入模塊,使用 Object
和 unrealized
這兩個全局變量
實驗-抽象工廠
接下來實現(xiàn)抽象工廠模式。抽象工廠能創(chuàng)建一系列相關(guān)的對象,而無需指定其具體類。
考慮如下情況,有多類敵人(正方形、圓形、長條),敵人初始化是兩種狀態(tài)的一種(正常狀態(tài),厚血狀態(tài)),且后期敵人和狀態(tài)種類還會增多
我們先定義敵人抽象類
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
分別實現(xiàn)白色工廠和紅色工廠(如果沒有額外的創(chuàng)建操作,可以不用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程序設(shè)計·第四版》羅伯托·耶魯薩林斯希 、第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("未實現(xiàn)", 3)
end
-- return Object
QUESTION1
如果不復(fù)制元方法,假設(shè)類B繼承類A,類B的對象實例b,b的元表是類B,在調(diào)用 b + b 時,涉及到算術(shù)運算符相關(guān)的元方法,b會在父類B中查找__add
,找不到并不會順著B的元表__index
再去B的父類A找,因此會報錯文章來源:http://www.zghlxwxcb.cn/news/detail-671329.html
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 ?
]]
點我返回文章來源地址http://www.zghlxwxcb.cn/news/detail-671329.html
到了這里,關(guān)于[Lua] 實現(xiàn)所有類的基類Object、模擬單繼承OO、實現(xiàn)抽象工廠的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!