В С++ это довольно привычно, располагать каждый класс в отдельном файле (обычно в двух, ИмяКласса.h и ИмяКласса.cpp). В Lua, поверьте, это тоже удобно :).
Проект удобно организовать следующим образом:
.
..
<luamodules> - папка
<cmodules> - папка
start.lua
...
Пути к Lua-библиотекам и С-библиотекам (загрузчики) можно прописать двумя различными способами.
Первый способ:
В первых двух строчках файла start.lua написать:
package.path = './luamodules/?.lua' -- пути к Lua библиотекам
package.cpath = './cmodules/?.dll' -- пути к С библиотекам
Второй путь:
Использовать системные переменные.
Создать файл start.bat в котором написать следующее:
set LUA_PATH=.\luamodules\?.lua;
set LUA_CPATH=.\cmodules?.dll
lua.exe start.lua
а в самом файле start.lua уже ничего не писать.
Итак, повторим наш учебный проект, но теперь с использованием модулей.
Первый модуль назовем Factory. Он будет создавать объекты классов и устанавливать метатаблицы.
Создадим файл Factory.lua (мы еще помним, что при поиске модуля Factory в шаблон package.path будет подставляться имя модуля?). Сохраним его в папке ./luamodules. В этом файле напишем следующее:
local base = _G
module('Factory')
function setBaseClass(class, baseClass)
base.assert(baseClass.mtab)
base.setmetatable(class, baseClass.mtab)
end
function create(class, ...)
local w = {}
setBaseClass(w, class)
w:construct(base.unpack(arg))
return w
end
В начале файла мы видим магическую строку
local base = _G
Зачем она? Функция module, если вспомнить вышесказанное о ней, во время своего выполнения переключает локальный контекст. Это означает, что после ее вызова, если ничего не предпринять, доступ к глобальным переменным Lua будет закрыт. Поэтому мы сохраняем специальную глобальную переменную Lua _G (_G._G = _G) в локальной переменной base. Теперь доступ к глобальным переменным осуществляется через base.имя_переменной (в нашем случае это base.assert, base.setmetatable и base.unpack).
Так, прочный фундамент мы заложили, приступим к надстройке. Создадим файл ./luamodules/Base.lua
local base = _G
module('Base')
mtab = { __index = _M }
local Factory = base.require('Factory')
function new()
return Factory.create(_M)
end
function construct(self)
base.print('Base created!')
self.field = 'text'
end
function setField(self, field) -- метод получения значения поля field
self.field = field
end
function getField(self) -- метод установки значения поля field
return self.field
end
Проясним некоторые места:
local Factory = base.require('Factory')
Для работы нам будет нужен модуль Factory.
Внимание!
Для избежания ошибок с циклическими зависимостями модулей, все функции require нужно вызывать после выполнения функции module!
mtab = { __index = _M }
Вспоминаем про функцию module - _M - это ссылка на саму таблицу Base.
mtab = { __index = _M }
Таблица mtab будет установлена как метатаблица у нового объекта класса Base при вызове метода create() модуля Factory. Это означает, что при поиске полей внутри объекта класса Base, если поле будет не найдено, то поиск будет осуществляться в таблице, на которую ссылается поле метатаблицы __index.
function new()
return Factory.create(_M)
end
Для удобства. Конечно, в прикладном коде можно вызвать и
local base = Factory.create(Base)
но мне кажется что вызов
local base = Base.new()
короче и очевидней.
function construct(self)
base.print('Base created!')
end
Эта функция будет вызвана из метода create() модуля Factory. Фактически, это конструктор класса.
Все методы класса имеют первым параметром ссылку на объект класса self.
Напишем тестовый скрипт ./start.lua:
package.path = './luamodules/?.lua' -- пути к Lua библиотекам
package.cpath = './cmodules/?.dll' -- пути к С библиотекам
require('Base')
local base = Base.new()
print(base:getField())
base:setField(1)
print(base:getField())
Запускаем его на выполнение.
lua.exe start.lua
Должны получить:
Base created!
text
1
Отлично! Двигаемся дальше. Создадим файл ./luamodules/Child.lua
local base = _G
module('Child')
mtab = { __index = _M }
local Factory = base.require('Factory')
local Base = base.require('Base')
Factory.setBaseClass(_M, Base) -- устанавливаем Base как базовый класс
function new(param1, param2) -- передаем параметры в конструктор
return Factory.create(_M, param1, param2)
end
function construct(self, param1, param2) -- конструктор с параметрами
Base.construct(self) -- вызов конструктора базового класса
base.print('Child created!', param1, param2)
end
function getField(self) -- переопределяем метод
return 'zzz'
end
Модернизируем немного ./start.lua, добавив в него строки:
require('Child')
и
local child = Child.new(1, 2)
print(child:getField())
child:setField(1)
print(child:getField())
Запускаем его на выполнение.
lua.exe start.lua
Должны получить:
Base created!
text
1
Base created!
Child created! 1 2
zzz
zzz
Завершающие штрихи. Как создасть статический член класса? Очень просто. Внутри модуля объявить его либо как
member = 1
либо (предпочтительнее, поскольку более очевидно чего вы хотите)
_M.member = 1
Соответственно, статический метод класса будет без первого параметра self:
function staticFun(param1, param2)
end
Файлы проекта:
Перед публикацией, советую ознакомится с правилами!
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
Нашли ошибку?
Вы можете сообщить об этом администрации.
Выделив текст нажмите CTRL+Enter