Модул:Миддлецласс/док
Ово је документациона подстраница за Модул: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АРЕ.