Руководство по Lua

Руководство по Lua

Статьи


ООП на модулях

В С++ это довольно привычно, располагать каждый класс в отдельном файле (обычно в двух, ИмяКласса.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

Файлы проекта: 

modulestest.zip
  • Файл: zip
  • Вес: 1.65 Kb
  • Загрузок: 13
Скачать



Работы в порту (грузчик и развозчик)
Работы в порту (грузчик и развозчик)
5-02-2021, 12:00, Скрипты
Смена производителя шин
Смена производителя шин
7-12-2020, 21:22, Скрипты
Cистема уровней
Cистема уровней
22-12-2020, 12:00, Скрипты
Телепорт-панель на DGS
Телепорт-панель на DGS
6-12-2020, 19:16, Скрипты
Movie
В данной публикации отсутствуют комментарии !

Перед публикацией, советую ознакомится с правилами!