OOP (Object Orientated Programming) / Metatables has always been an issue for me. To keep it simple, I just don’t understand it at all.
Many have told me to look at documentations either from Roblox or from lua.org however I simply unable to understand and I don’t really have anyone to actively ask my questions so I’ve decided to just pile up the questions in this one post. I have many questions regarding why I should use OOP and under what circumstances it should be used.
Under what circumstances should this be used?
Why should I use it?
I could achieve most of the functionalities in OOP using normal tables, are metatables better then tables?
Are meta tables save able in datastore? (One time when I was saving a inventory system I was told to learn meta tables.)
Do I have to clean up the metatables when I don’t need them like setting it to nil?
How does self work?
How would I be able to re-reference the table inside the table?
local table1 = {
properties = {
a = 1
}
Function = function()
print(self.properties.a) -- I'm trying to print 1 inside the table
end
}
I’ve searched the forums to see if there were any tutorials but I wasn’t able to find any. I feel like I’ve just hit a roadblock and unable to continue.
If you can’t think of a use-case for OOP, you likely don’t need to use it. OOP is heavily overrated and only really should be used where having classes makes sense in your code-base.
A metatable is simply special instructions for how to do a certain thing, nothing special.
As far as I’m aware, no.
No. Metatables are just instructions that are attached to a table.
e.g.
local array = {"a", "b", "c"}
setmetatable(array, {
__tostring = function() -- the tostring metamethod is useful for debugging
return "<array>"
end
})
print(array) -- this outputs '<array>' as print uses tostring under the hood
Within a : method (See below), you have access to the self global, it’s a direct reference to the object the function is under.
local methods = {}
function methods.dot()
-- as this is using the 'dot', we do not have access to self
-- although we could use the 'methods' variable directly as a
-- substitute.
end
function methods:colon()
print(self == methods) --> true
end
-- these two are functionally identical
methods:colon()
methods.colon(methods)
methods:dot()
-- you should avoid using colons for methods that
-- are not a part of objects. not 100% sure what'd happen here.
-- as per convention, constructors and non-class-methods should
-- use the dot, whereas, actual class methods (that would need self)
-- should use a :
Your code example is a bit unclear. From my understanding, you want to do this:
local object = {}
object.properties = { a = 1 }
-- if using 'classes', this would be very useful
-- when using singletons, not so much.
function object:printA()
print(self.properties.a)
end
Using OOP in practise
Here’s an example of a class:
Counter.lua
local Counter = {}
-- 'index' means that Roblox will fallback to the 'Counter'
-- table if something is indexed that is nil
Counter.__index = Counter
function Counter.new()
-- as we're not using a :, 'self' is just a regular variable
-- no globals are being overriten here!
local self = {
number = 0
}
-- You could just do {__index = Counter} here iirc
-- not sure why you index the actual object, oh well
setmetatable(self, Counter)
-- We are returning an object with a metatable!
return self
end
-- As this has a :, we can use 'self'
function Counter:count()
self.number = self.number + 1
return self.number
end
return Counter
And then, we can use our new class like so:
-- like any module, we must require it.
local Counter = require(path_to_counter)
-- we can create multiple versions of our counter!
-- each of these will have their own independent variables
local counterA = Counter.new()
local counterB = Counter.new()
local counterC = Counter.new()
-- for loop to speed stuff up lol
-- not the most efficent code
for i = 1, 100 do
if i > 50 then
counterA:count()
end
if i < 25 then
counterB:count()
end
counterC:count()
end
print(counterA.number) --> 50
print(counterB.number) --> 24
print(counterC.number) --> 100
Although all of these are on the same module, we have multiple different revisions with little overhead on our part.
Further Reading
I just covered the basics of OOP. But there is so much more! including sub classes (whaaaa?)
I personally suggest this tutorial: (although you can use the search tool to find many more topics about OOP!)