Модуль:Class

Материал из свободной русской энциклопедии «Традиция»
Перейти к навигации Перейти к поиску
{
   [1] = 2,
   [2] = 3,
   [3] = 5

}


--[[
Функция, создающая классы с множественным наследованием.
Источник: http://lua-users.org/wiki/SimpleLuaClasses.

	Создание класса: MyClass = require "Модуль:Class".create {атрибуты}

	_init	— функция конструирования,
	_call	— перегрузка круглых скобок для экземпляров класса.
	_index	— перегрузка квадратных скобок для экземпляров класса.
--]]
 
-- Dependencies:
local clone = (mw or require 'mw.lua').clone
local serialise = (require 'Module:Test' or require 'Test.lua').serialise

local m = {}

m.create = function (...)
	local c		= {}    -- a new class instance.
	c._base		= {}	-- список базовых классов
	c._inits	= {}	-- конструкторы базовых классов и собственный.
	
	local own_init		-- собственный конструктор
	-- В общем случае, множественное наследование:
	local args = {...}
	for _, param in ipairs (args) do
		if		type (param) == 'function' then
			-- Этот параметр -- конструктор:
			own_init = param
		elseif	type (param) == 'table'		then
			-- Это один из базовых классов. Сделать его глубокую копию:
			c = clone (param)
			c._base = param._base or {}
			c._inits = param._inits or {}
			-- и запомнить его конструктор(ы) и дописать к списку базовых классов:
			c._base [#c._base + 1] = param
		end
	end	-- for _, param in ipairs (arg)
	-- 	Дописываем к списку конструкторов собственный, чтобы он вызвался последним:
	c._inits [#c._inits + 1] = own_init
	
	-- the class will be the metatable for all its objects,
	-- and they will look up their methods in it.
	c.__index = c
	
	-- expose a constructor which can be called by <classname>(<args>)
	local mt = {}
	mt.__call = function (class_tbl, ...)
		local obj = {}
		for key, field in pairs (c) do
			if type (field) ~= 'function'
				and key ~= '_call' and key ~= '_index' and key ~= '__index' and key ~= '_base' and key ~= '_inits' then
				obj [key] = mw.clone (field)
			end
		end
		setmetatable (obj, c)

		-- Запись перегруженного оператора () (class:_call ()) в экземпляр класса.
		--     Только к моменту первого вызова конструктора _call окончательно установлена,
		--     но устанавить __call для метатаблицы нужно только в первый раз:
		if not c.__call and class_tbl._call and type (class_tbl._call) == 'function' then
			c.__call = class_tbl._call
		end
		-- Аналогично с class:_index ():
		if not c.__index and class_tbl._index and type (class_tbl._index) == 'function' then
			c.__index = class_tbl._index
		end		
		 
		-- Вызов конструкторов базовых классов, и в конце собственного:
		for _, init in ipairs (class_tbl._inits) do
			if type (init) == 'function' then
	    		init (obj, ...)
	    	end
	    end

		return obj
	end

	-- Внесение _init () в таблицу _inits:
	mt.__newindex = function (class_tbl, key, value)
		if key == '_init' and type (value) == 'function' then
			class_tbl._inits [#class_tbl._inits + 1] = value
		else
			rawset (class_tbl, key, value)
		end
	end	-- mt.__newindex = function (class_tbl, key, value)

	c.is_a = function (self, class)
		if not class then
			return false
		end
		
		local m = getmetatable (self)
		if m == class then
			-- объект непосредственно этого класса:
			return true
		end
		
		-- Проверка базовых классов:
		for _, b in ipairs (class._base or {}) do
			-- рекурсивный вызов:
			if c.is_a (self, b) then
				return true
			end
		end
		return false
	end	-- c.is_a = function (self, class)
 
	setmetatable (c, mt)
	return c
end

m.test = function ()
	local Array = (require 'Module:Array' or require 'Array.lua').Array
	local arr = Array (2, 3, 5)
	return serialise (arr)
end

return m