在先前的章節中,我們都將程式碼寫在同一個檔案中,隨著專案規模上升,若我們仍然用這種方式撰寫程式碼,程式碼會變得很長而不易維護。將程式碼依照功能拆開在不同的模組中,對於專案的維護會較佳。
我們在前文中提過,Lua 是一個內嵌語言,可用的套件較其他語言來得少,而且會隨著宿主軟體而有所不同;通常會在宿主軟體中透過外掛 (plugins) 的方式來延伸 Lua 的功能。有些功能,像是某個遊戲所需的規則和人工智能,會隨著不同遊戲而異,不太會由宿主軟體提供。通常程式設計者會自行撰寫一些模組,再自行呼叫該模組。
撰寫模組
我們直接以實例展示如何撰寫 Lua 模組:
-- point.lua
local Point = {}
package.loaded["Point"] = Point
Point.__index = Point
function Point:new(x, y)
self = {}
self._x = x
self._y = y
setmetatable(self, Point)
return self
end
function Point:x()
return self._x
end
function Point:y()
return self._y
end
return Point
一個模組包含單一類別,而類別的撰寫方式如同前文所述。一般來說,我們會將物件寫在一個表 (table) 中。雖然在使用表時,無法區分公有和私有的屬性和方法,但這不是 Lua 程式的重點,不用在這個環節上過度計較。
通常在第二行會加上 package.loaded[...] = ...
的敘述,告訴 Lua 將此模組載入。
常見的手法是將 __index
這個 metamethod 指向物件本身。這樣的意義在於當 Lua 找不到某個特定屬性或函式時,會從該物件的表中去查找。
接下來就如同一般寫物件的方式,此處不再重覆。
在最後一行會將此類別回傳,待外部程式呼叫。在模組內也可以呼叫其他模組,在下一段介紹如何呼叫模組。
使用模組
承接以上的例子,我們直接以實例展示如何呼叫模組:
local point = require("point")
local p = point:new(3, 4)
assert(p:x() == 3)
assert(p:y() == 4)
使用 require
即可呼叫模組,預設情形下,模組和主程式位在同一個資料夾。若模組位於子目錄下,則略為修改 require
的位置即可,如下:
local point = require("lib.point")
local p = point:new(3, 4)
assert(p:x() == 3)
assert(p:y() == 4)