Пређи на садржај

Модул:Миддлецласс/док

Izvor: Vojna Enciklopedija
(разл) ← Старија измена | Тренутна верзија (разл) | Новија измена → (разл)

Ово је документациона подстраница за Модул:Middleclass

Миддлецласс ис ан објецт-ориентед либрарy фор Луа, wриттен бy Енриqуе Гарцíа Цота (кикито) анд маинтаинед ат ГитХуб.

Иф yоу аре фамилиар wитх Објецт Ориентатион ин отхер лангуагес (C++, Јава, Рубy … ) тхен yоу wилл пробаблy финд тхис либрарy верy еасy то усе.

Тхе цоде ис реасонаблy цомментед, со yоу мигхт wант то емплоy соме тиме ин реадинг тхроугх ит, ин ордер то гаин инсигхт оф Луа’с амазинг флеxибилитy.

Qуицк Еxампле

local class = require('Module:Middleclass').class

Person = class('Person') --this is the same as class('Person', Object) or Object:subclass('Person')

function Person:initialize(name)
	self.name = name
end

function Person:speak()
	return 'Hi, I am ' .. self.name ..'.'
end

AgedPerson = class('AgedPerson', Person) -- or Person:subclass('AgedPerson')
AgedPerson.static.ADULT_AGE = 18 --this is a class variable

function AgedPerson:initialize(name, age)
	Person.initialize(self, name) -- this calls the parent's constructor (Person.initialize) on self
	self.age = age
end

function AgedPerson:speak()
	local hi = Person.speak(self) -- "Hi, I am xx."
	if self.age < AgedPerson.ADULT_AGE then -- accessing a class variable from an instance method
		return hi .. '\nI am underaged.'
	else
		return hi .. '\nI am an adult.'
	end
end

local p1 = AgedPerson:new('Billy the Kid', 13) -- this is equivalent to AgedPerson('Billy the Kid', 13) - the :new part is implicit
local p2 = AgedPerson:new('Luke Skywalker', 21)
mw.log(p1:speak())
mw.log(p2:speak())

Оутпут:

Hi, I'm Billy the Kid.
I am underaged.
Hi, I'm Luke Skywalker.
I am an adult.

Цомпаринг цлассес анд објецтс

Yоу цан цомпаре цлассес анд објецтс wитх тхе буилт-ин метходс isInstanceOf, isSubclassOf, анд includes.

obj:isInstanceOf(MyClass)
aClass:isSubclassOf(Object)
aClass:includes(aMixin)

Цаутион: тхис цоде wилл тхроw ан еррор иф обј ис нот ан објецт, ор иф аЦласс ис нот а цласс (синце тхеy wилл нот имплемент исИнстанцеОф, исСубцлассОф ор инцлудес). Иф yоу аре унсуре оф wхетхер обј анд аЦласс аре ан објецт ор а цласс, yоу цан усе тхе метходс ин Објецт. Тхис ис а специал објецт тхат цан бе обтаинед фром тхе цласс објецт.

Ит ис поссибле то гет а специал објецт Објецт фром тхе цласс фунцтион.

local middleclass = require('Module:Middleclass')
local class = middleclass.class
local Object = class.Object

print(Object) -- prints 'class Object'

Тхе метходс ин Објецт аре препаред то wорк wитх рандом тyпес, нот јуст цлассес анд инстанцес:

Object.isInstanceOf(obj, MyClass)
Object.isSubclassOf(aClass, Object)
Object.includes(aClass, aMixin)

Метаметходс

Метаметходс цан до функy стуфф лике аллоwинг аддитионс ин оур инстанцес.

Лет’с маке ан еxампле wитх __tostring

Point = class('Point')

function Point:initialize(x,y)
	self.x = x
	self.y = y
end

function Point:__tostring()
	return 'Point: [' .. tostring(self.x) .. ', ' .. tostring(self.y) .. ']'
end

p1 = Point(100, 200)
p2 = Point(35, -10)
mw.log(p1)
mw.log(p2)

Оутпут:

Point: [100, 200]
Point: [35, -10]

Тхе цомплете лист оф суппортед метходс цан бе сеен он миддлецласс’ соурце цоде:

'__add', '__call', '__concat', '__div', '__le', '__lt', '__mod', '__mul', '__pow', '__sub', '__tostring', '__unm'

Yоу маy нотице тхат __index ис миссинг. Тхат метаметход ис ресервед фор тхе либ анд цан’т бе усед. Он миддлецласс 1.x тхере wас а wаy ароунд тхис (виа а миxин) бут фор ноw I дон’т рецомменд усинг тхис метаметход wитх цлассес. Yоу wилл пробаблy бе беттер офф усинг а пуре Луа табле инстеад, иф yоу неед тхат.

Ноте абоут Луа 5.2: Тхис неw версион оф Луа аддс суппорт фор 3 неw метаметходс __len, __pairs & __ipairs. Тхесе метходс аре ноw алсо инцлудед ин миддлецласс. Бут yоу wилл неед то бе руннинг Луа 5.2 (ор Луајит цомпилед wитх тхе DLUAJIT_ENABLE_LUA52COMPAT флаг) ин ордер то бе абле то усе тхем.

Миxинс

Миxинс цан бе усед фор схаринг метходс бетwеен цлассес, wитхоут реqуиринг тхем то инхерит фром тхе саме фатхер.

local class = require 'middleclass'
HasWings = { -- HasWings is a module, not a class. It can be "included" into classes
fly = function(self)
	return 'flap flap flap I am a ' .. self.class.name
end
}

Animal = class('Animal')

Insect = class('Insect', Animal) -- or Animal:subclass('Insect')

Worm = class('Worm', Insect) -- worms don't have wings

Bee = class('Bee', Insect)
Bee:include(HasWings) --Bees have wings. This adds fly() to Bee

Mammal = class('Mammal', Animal)

Fox = class('Fox', Mammal) -- foxes don't have wings, but are mammals

Bat = class('Bat', Mammal)
Bat:include(HasWings) --Bats have wings, too.

local bee = Bee() -- or Bee:new()
local bat = Bat() -- or Bat:new()
mw.log(bee:fly())
mw.log(bat:fly())

Оутпут:

flap flap flap I am a Bee
flap flap flap I am a Bat

Инцлудед

Миxинс цан провиде а специал фунцтион цаллед 'инцлудед'. Тхис фунцтион wилл бе инвокед wхен тхе миxин ис инцлудед он а цласс, аллоwинг тхе программер то до ацтионс. Тхе онлy тwо параметерс аре тхе миxин (нормаллy имплицит) анд тхе цласс.

local class = require('Module:Middleclass').class
DrinksCoffee = {}

-- This is another valid way of declaring functions on a mixin.
-- Note that we are using the : operator, so there's an implicit self parameter
function DrinksCoffee:drink(drinkTime)
	if(drinkTime~=self.class.coffeeTime) then
		return self.name .. ': It is not the time to drink coffee!'
	else
		return self.name .. ': Mmm I love coffee at ' .. drinkTime
	end
end

-- the included method is invoked every time DrinksCoffee is included on a class
-- notice that paramters can be passed around
function DrinksCoffee:included(klass)
	mw.log(klass.name .. ' drinks coffee at ' .. klass.coffeeTime)
end

EnglishMan = class('EnglishMan')
EnglishMan.static.coffeeTime = 5
EnglishMan:include(DrinksCoffee)

function EnglishMan:initialize(name)
	self.name = name
end

Spaniard = class('Spaniard')
Spaniard.static.coffeeTime = 6
Spaniard:include(DrinksCoffee)

function Spaniard:initialize(name)
	self.name = name
end

tom = EnglishMan:new('tom')
juan = Spaniard:new('juan')

mw.log(tom:drink(5))
mw.log(juan:drink(5))
mw.log(juan:drink(6))

Оутпут:

EnglishMan drinks coffee at 5
Spaniard drinks coffee at 6
tom: Mmm I love coffee at 5
juan: It is not the time to drink coffee!
juan: Mmm I love coffee at 6

Наминг Цонвентионс

Wхат фоллоwс ис а сет оф рецоммендатионс усед фор наминг објецтс wитх МиддлеЦласс. Тхесе аре цомплетелy оптионал, бут wилл бе усед ин алл тхе модулес дирецтлy депендент он МиддлеЦласс (суцх ас миддлецласс-еxтрас ор ПÄССИОН)

Цлассес анд пацкагес

  • Пацкаге намес схоулд бе лоwерцасед анд сепаратед бy ундерсцорес: пацкаге_наме
  • Цласс намес & миxин намес схоулд бегин wитх Упперцасе, анд усе цамелЦасе нотатион: МyЦласс, МyМиxин
  • Оне еxцептион то тхис руле ис wхен цлассес аре децларед инсиде пацкагес ин тхат цасе, тхеy цан бе децларед ас фоллоwс:
MyClass = class('package.MyClass')
  • Анотхер еxцептион ис фор интернал цлассес (цлассед децларед инсиде цлассес)
MyClass = class('package.MyClass')
MyClass.InternalClass = class('package.MyClass.InternalClass')

Аттрибутес, инстанцес анд цонстантс

  • Аттрибутес бегин wитх лоwерцасе, анд усе цамелЦасе: МyЦласс.аттрибутеОне, МyЦласс.аттрибутеТwо
    • Ан ундерсцоре цан прецеде тхе инитиал лоwерцасе иф тхе аттрибуте ис суппосед то бе привате: МyЦласс._приватеАттрибуте
  • Вариаблес поинтинг то инстанцес оф цлассес схоулд старт wитх лоwерцасе анд бе цамелЦасед: мyИнстанце = МyЦласс:неw()
  • Цонстантс схоулд бе АЛЛ_УППЕРЦАСЕД, анд усе ундерсцорес фор wорд сепаратионс: МyЦласс.МY_ЦОНСТАНТ
  • Привате аттрибутес схоулд бе прецедед wитх ан ундерсцоре: _мyПриватеАттрибуте

Метходс

  • Метходс схоулд бегин wитх лоwерцасе, анд усе цамелЦасе: МyЦласс.мyМетход
  • Wхен поссибле, метходс схоулд усе еxплицит вербс: гетX() инстеад оф x()
  • Инстанце метходс схоулд бе децларед усинг тхе цолонс, со тхеy хаве ан имплицит ‘селф’ параметер:
function MyClass:setX(x)
	self.x = x
end
  • Цласс метходс схоулд усе тхе специал пропертy ‘статиц’ фор беинг дефинед:
function MyClass.static:classMethod()
	return 'I am the ' .. self.name .. ' class. I am awesome'
end
  • Привате метходс схоулд бе прецедед wитх ан ундерсцоре: _myPrivateMethod

Филе намес

  • Ин генерал, филенамес wилл бе намед усинг семицолонс анд лоwерцасе леттерс: мy-филе-наме.луа
  • Фолдерс цонтаининг а пацкаге схоулд бе цаллед тхе саме wаy ас тхе пацкаге итселф: мy_пацкаге схоулд бе сторед ундер а фолдер цаллед /мy-пацкаге/
  • Иф а филе онлy дефинес а цласс, ит схоулд бе намед лике тхе цласс, инцлудинг лоwерцасе/упперцасе: а цласс намед МyЦласс схоулд бе дефинед он а филе намед МyЦласс.луа
  • Саме хаппенс wитх Миxинс: тхе филе дефининг МyМиxин схоулд бе цаллед МyМиxин.луа
  • Иф а цласс ис со биг ит неедс то бе сплит он северал филес, прецеде алл тхе филес дефининг тхис цласс wитх тхе цласс наме, фоллоwед бy ан ундерсцоре анд ан еxпланатион оф wхат тхе филе дефинес:
Game.lua
Game_MainMenu.lua
Game_OptionsMenu.lua
Game_Play.lua

Привате мемберс

Тхере аре северал wаyс yоу цан маке привате параметерс wитх миддлецласс.

Ундерсцоринг

Тхе симплест оне ис јуст то прецеде yоур аттрибутес wитх ундерсцорес. Тхис ис ацтуаллy wриттен он тхе Луа 5.1 референце, сецтион 2.1, “Леxицал цонверсионс”, ас а wаy то саy “тхис ис хере, бут плеасе дон’т усе ит”.

local class = require('middleclass')
MyClass = class('MyClass')

function MyClass:initialize()
	self._internalStuff = 1
	self.publicStuff = 2
end

Хоwевер, тхис исн’т реаллy макинг тхе пропертиес “хидден”.

Привате цласс аттрибутес

Ин генерал, тхе wаy оф “реаллy” геттинг хидден фунцтионс ор вариаблес цонсистс он усинг Луа’с сцопинг рулес. Тхе симплест wаy оф усинг тхис ис цреатинг еацх оф yоур цлассес он сепарате филес, анд тхен децларинг анy привате вариабле ор фунцтионс ас лоцал, он тхе “роот” сцопе оф тхе филе.

Еxампле:

-- File 'MyClass2.lua'
local class = require('middleclass')

MyClass2 = class('MyClass2')

local _internalClassCounter = 0

function MyClass2:initialize()
	_internalClassCounter = _internalClassCounter + 1
	self.publicStuff = 2
end

function MyClass2:getCount()
	return(_internalClassCounter)
end

Тхе сцопе оф лоцал децларатионс он а луа филе ис тхе филе итселф. Иф yоу децларе сометхинг “лоцал” ин оне филе ит ис нот аваилабле он отхерс, евен иф тхеy “реqуире” тхат филе.

-- File 'main.lua'

require('MyClass2')

-- Try to change internal member...
_internalClassCounter = 4 -- Attempt to modify the _internalClassCounter variable

mw.log(MyClass2:getCount()) -- prints "0"

Лет ме еxплаин wхат хаппенс хере. Тхе _интерналЦлассЦоунтер = 4 лине ис, ин реалитy, цреатинг а неw глобал вариабле цаллед интерналЦлассЦоунтер, анд ассигнинг ит 4. Тхе “реаллy интернал” оне ис “оут оф реацх” он маин.луа (унлесс сомеоне доес реаллy трицкy стуфф wитх тхе енвиронментс). Со гетЦоунт() wоркс ас еxпецтед.

Привате инстанце метходс

Ит ис алсо поссибле то децларе привате метходс. Тхе трицк хере ис нот то “инцлуде” тхем он тхе цласс дефинитион. Он тхе фоллоwинг еxампле, wе wилл нот децларе ит он Class3:secretMethod; инстеад wе’лл цреате а лоцал фунцтион. Синце wе’ре нот усинг тхе : оператор анy море, wе хаве то маке тхе “селф” параметер еxплицит. Алсо, синце wе хаве то маке ит лоцал, wе хаве то девиате фром тхе “усуал” wаy оф децларинг Луа фунцтионс (тхе “усуал” wаy оф децларинг фунцтионс макес тхем глобал):

-- File 'MyClass3.lua'
local class = require('middleclass')

MyClass3 = class('MyClass3')

local _secretMethod = function(self) -- notice the 'local' at the beginning, the = function and explicit self parameter
	return 'My name is ' .. self.name .. ' and I have a secret.'
end

function MyClass3:initialize(name)
	self.name = name
end

function MyClass3:shout()
	return _secretMethod(self) .. ' You will never know it!' 
end


-- File 'Main.lua'
require('MyClass3')

peter = MyClass3:new('peter')
mw.log(peter:shout()) -- My name is peter and I have a secret. You will never know it!

mw.log(_secretMethod(peter)) -- throws an error - _secretMethod is nil here.

Тхис тецхниqуе алсо аллоwс тхе цреатион оф привате цласс метходс. Ин МиддлеЦласс, тхере’с реаллy но дифференце бетwеен цласс метходс анд инстанце метходс; тхе дифференце цомес фром wхат yоу пасс то тхеир ‘селф’ параметер. Со иф yоу инвоке _сецретМетход лике тхис: _сецретМетход(МyЦласс3) ит wилл бе а цласс метход.

А слигхтлy море еффициент wаy оф цреатинг а цласс метход wоулд бе геттинг рид оф тхе ‘селф’ параметер анд усе МyЦласс3 дирецтлy он тхе метход’с бодy:

MyClass3 = class('MyClass3')

local _secretClassMethod = function() -- self parameter out
	return 'My name is ' .. MyClass3.name .. ' and I have a secret.' -- use MyClass3 directly.
end

-- Note that this alternative is only recommended for private class methods. Public class methods should follow the convention of adding one explicit 'class' parameter:
MyClass3 = class('MyClass3')

function MyClass3.classMethod(theClass)
	return( 'Being a public class named ' .. theClass.name .. ' is not a bad thing.' )
end

Тхис гивес а бит море оф флеxибилитy wхен оверридинг публиц цласс метходс он субцлассес.

Финаллy, а субтле поинт регардинг рецурсиве привате метходс. Иф yоу неед то цреате а привате метход тхат цаллс химселф, yоу wилл неед то децларе тхе вариабле фирст, анд тхен (он тхе неxт лине) инитиализе ит wитх тхе фунцтион валуе. Отхерwисе тхе вариабле wилл нот бе аваилабле wхен тхе фунцтион ис цреатед

MyClass3 = class('MyClass3')

local _secretRecursiveMethod -- variable declared here
_secretRecursiveMethod= function(self, n) -- and initialized here
	if(n<=0) then
		return  'Last recursion'
	else
		print ( 'Recursion level ' .. n )
		_secretRecursiveMethod(self, n-1)
	end
end

function MyClass3:recurseOver(n)
	_secretRecursiveMethod(self, n)
end

m = MyClass3:new()
m:recurseOver(5)

Оутпут:

Recursion level 5
Recursion level 4
Recursion level 3
Recursion level 2
Recursion level 1
Last recursion

Привате Инстанце аттрибутес

Инстанце аттрибутес аре а литтле бит трицкиер то имплемент, синце wе онлy хаве оне сцопе то “хиде стуфф ин”, анд ит хас то цопе wитх мултипле инстанцес.

Оне wаy то до тхис ис усинг оне привате цласс вариабле ас а ‘стасх’. Иф yоу усе оне табле инстеад оф јуст а нумбер, yоу цан анд хиде тхере алл тхе привате информатион yоу маy неед. Оне проблем wитх тхис аппроацх ис тхат yоу неед то цоме оут wитх а ‘кеy’ пер ‘инстанце’.

Фортунателy тхис ис а верy симпле тхинг то до, синце ин луа yоу цан усе неарлy анy тyпе оф објецт ас а кеy – Со yоу цан усе тхе инстанцес тхемселвес ас кеyс. Ин отхер wордс, wе усе ‘селф’ ас а кеy.

Оне проблем wитх тхис аппроацх ис тхат инстанцес мигхт нот бе либератед бy тхе гарбаге цоллецтор онце тхеy аре нот усед анy море (синце тхере’с а референце то тхем он тхе ‘стасх’ кеyс). Ин ордер то авоид тхис, wе цан маке тхе ‘стасх’ а wеак табле.

Он тхе фоллоwинг еxампле, тхе наме аттрибуте ис публиц, бут аге анд гендер аре привате.

Оур ‘сецрет стасх’ ин тхе фоллоwинг еxампле wилл бе цаллед _привате.

-Бy тхе wаy, тхе фоллоwинг еxампле алсо схоwс хоw yоу цан до “реад-онлy-исх аттрибутес”: yоу маке тхем привате, анд маке геттерс фор тхем, бут нот сеттерс.

-- File 'MyClass4.lua'
local class = require('middleclass')

MyClass4 = class('MyClass4')

local _private = setmetatable({}, {__mode = "k"})   -- weak table storing all private attributes

function MyClass4:initialize(name, age, gender)
	self.name = name
	_private[self] = {
		age = age,
		gender = gender
	}
end

function MyClass4:getName() -- shorter equivalent: MyClass4:getter('name')
	return self.name
end

function MyClass4:getAge()
	return _private[self].age
end

function MyClass4:getGender()
	return _private[self].gender
end


-- File 'main.lua'

require('MyClass4')

stewie = MyClass4:new('stewie', 2, 'male')

mw.log(stewie:getName()) -- stewie
stewie.name = 'ann'
mw.log(stewie.name) -- ann

mw.log(stewie:getAge()) -- 2
stewie.age = 14        -- this isn't really modifying the age... it is creating a new public attribute called 'age'
-- the internal age is still unaltered
mw.log(stewie:getAge()) -- 2
-- the newly created external age is also available.
mw.log(stewie.age) -- 14

-- same goes with gender:

mw.log(stewie:getGender()) -- 'male'
stewie.gender = 'female'
mw.log(stewie:getGender()) -- 'male'
mw.log(stewie.gender) -- 'female'

Привате мемберс он тхе саме филе

Тхере’с алсо а wаy оф цреатинг привате мемберс тхат отхер цлассес/метходс он тхе саме филе цан’т аццесс, иф yоу евер хад тхе неед.

Јуст цреате ан артифициал сцопе wитх до … енд, анд децларе привате мемберс ас ‘лоцал’ инсиде тхат блоцк. Онлy тхе метходс инсиде тхат блоцк wилл хаве аццесс то тхем:

-- File 'MyClass3.lua'
local class = require('middleclass')

MyClass3 = class('MyClass3')

function MyClass3:initialize(name)
	self.name = name
end

do
	local secretMethod = function(self) -- notice the explicit self parameter here.
		return 'My name is ' .. self.name .. ' and I have a secret.'
	end

	function MyClass3:shout()
		return secretMethod(self) .. ' You will never know it!'
	end
end

-- functions outside the do-end will not 'see' secretMethod, but they will see MyClass3.shout (because they see MyClass3)

МИТ Лиценсе

Цопyригхт (ц) 2011 Енриqуе Гарцíа Цота

Пермиссион ис херебy грантед, фрее оф цхарге, то анy персон обтаининг а цопy оф тхис софтwаре анд ассоциатед доцументатион филес (тхе "Софтwаре"), то деал ин тхе Софтwаре wитхоут рестрицтион, инцлудинг wитхоут лимитатион тхе ригхтс то усе, цопy, модифy, мерге, публисх, дистрибуте, сублиценсе, анд/ор селл цопиес оф тхе Софтwаре, анд то пермит персонс то wхом тхе Софтwаре ис фурнисхед то до со, субјецт то тхе фоллоwинг цондитионс:

Тхе абове цопyригхт нотице анд тхис пермиссион нотице схалл бе инцлудед ин алл цопиес ор субстантиал портионс оф тхе Софтwаре.

ТХЕ СОФТWАРЕ ИС ПРОВИДЕД "АС ИС", WИТХОУТ WАРРАНТY ОФ АНY КИНД, ЕXПРЕСС ОР ИМПЛИЕД, ИНЦЛУДИНГ БУТ НОТ ЛИМИТЕД ТО ТХЕ WАРРАНТИЕС ОФ МЕРЦХАНТАБИЛИТY, ФИТНЕСС ФОР А ПАРТИЦУЛАР ПУРПОСЕ АНД НОНИНФРИНГЕМЕНТ. ИН НО ЕВЕНТ СХАЛЛ ТХЕ АУТХОРС ОР ЦОПYРИГХТ ХОЛДЕРС БЕ ЛИАБЛЕ ФОР АНY ЦЛАИМ, ДАМАГЕС ОР ОТХЕР ЛИАБИЛИТY, WХЕТХЕР ИН АН АЦТИОН ОФ ЦОНТРАЦТ, ТОРТ ОР ОТХЕРWИСЕ, АРИСИНГ ФРОМ, ОУТ ОФ ОР ИН ЦОННЕЦТИОН WИТХ ТХЕ СОФТWАРЕ ОР ТХЕ УСЕ ОР ОТХЕР ДЕАЛИНГС ИН ТХЕ СОФТWАРЕ.