Алексей++, с уточнением, что внутри объекта есть разница между замыканием и ассоциативным массивом как точкой доступа.
-- конструктор объекта
function createC()
-- локальные переменные функции createC сохранятся внутри замыкания и не будут видны снаружи
-- так делаются private члены
-- чтобы их отличать от public, локальных переменных и аргументов методов, будем им ставить префикс "_"
-- private поля и сразу их инициализация как в конструкторе, код инициализации может быть произвольной сложности
local _x = 1
-- protected static поля - общие для всех объектов
_Const = 3
-- private методы
function _printState()
print(_x)
end
-- ассоциативный массив для создаваемого объекта, являющийся точкой доступа к объекту
-- содержимое ассоциативного массива хранит public члены объекта
local self = {}
-- public поля и сразу их инциализация, например, для объявления доступных снаружи констант, которые будем писать с большой буквы
self.Const = _Const
-- public методы
function self.set(x)
-- можем работать с private членами, поскольку функция находится в общем замыкании с переменными
_x = x
_printState()
end
function self.get()
return _x
end
-- конструктор обязательно возвращает ассоциативный массив с открытыми членами объекта
return self
end
-- использование
o = createC()
-- теперь в переменной o находится ассоциативный массив с открытыми членами объекта,
-- со всеми функциями внутри этого массива связано замыкание конструктора, в котором хранятся закрытые члены объекта
o.set(o.Const)
print(o.get())
3
3
Таким образом, функция createC здесь не вполне фабрика. Она не ограничивается только созданием ассоциативного массива со связанными между собой элементами, она ещё генерирует собственное замыкание с закрытыми внутри него private переменными и вспомогательными функциями и увязывает всю эту конструкцию в единый объект.
Стоит заметить, что конструктор может порождать целую серию ассоциативных массивов, являющихся разными интерфейсами одного и того же объекта. Например
function createProcessor
local readerInterface = {}
function readerInterface.read()
print("read")
end
local writerInterface = {}
function writerInterface.write()
print("write")
end
return readerInterface, writerInterface
end
reader, writer = createProcessor()
reader.read()
writer.write()
read
write
Наследование с полиморфизмом поведения реализуется элементарно. Достаточно получить ассоциативный массив предка и дополнить/переписать его под потомка. При этом private-члены будут свои для каждого конструктора. С одним ассоциативным массивом связывается несколько замыканий: по одному замыканию от каждого конструктора с local-переменными (private) и общее замыкание для всех конструкторов и всех объектов с не local переменными (static protected).
function createParent
local _x = 1
_y = 3
local self = {}
function self.test()
print("test")
end
function self.foo()
print("parent")
print(_x)
print(_y)
end
return self
end
function createChild
local _x = 2
local self = createParent()
function self.foo()
print("child")
print(_x)
print(_y)
end
return self
end
p = createParent()
p.test()
p.foo()
c = createChild()
c.test() -- унаследовано от parent
c.foo() -- переопределено, но переменная _y видна из parent и является общей для всех объектов
test
parent
1
3
test
child
2
3
В общем, вот и весь краткий курс безклассового ООП.