I want to make my code as secure as possible.
As such, I want to keep as much data as possible private.
Let’s hop straight to it. An example metatable-based instance might look something like this:
-- In a module script
function Module.new()
local self = {}
self.PrivateValue = math.random(1,3)
return setmetatable(self, Module)
end
function Module:GetPrivateValue()
return self.PrivateValue
end
In the above code, I can do Module.new() to make a new instance that contains a new random number. However, the issue is that PrivateValue can be directly read and set without any boundaries. I want GetPrivateValue to be the only way to get the PrivateValue number.
I usually not use OOP in Roblox, so it’s worth checking what @Club_Moo before trying mine. I know the concepts and I use it a lot in Java (where there is an option for private/public variables).
What I would do is make PrivateNumber a local variable, and not a part of the table. Then add the GetPrivateNumber function to the table from the Module.new(). But it might not be a good practice, so I am not sure…
I am not too familiar with OOP, so I probably used metatables incorrectly, read my next post.
-- In a module script
local Module = {}
local PrivateValue --Your private variable
function Module.new()
local self = {}
PrivateValue = math.random(1,3) --Assign value from constructor.
setmetatable(self, Module) --Make self a copy of Module
return getmetatable(self) --Return the copy
end
function Module:GetPrivateValue() --Add a Getter to this value
return PrivateValue
end
return Module
Script:
local library = require(script.ModuleScript)
local myObject = library.new()
print(myObject:GetPrivateValue())
print(myObject.PrivateValue)
As @Club_Moo notes, you can define the __index metamethod as a function to control precisely what is returned when you access a table.
However, do note that if you are using a lot of object-oriented practices in your code, it is much faster to leave __index equal to a table than define your own custom indexing solution. This is because of a Luau VM optimization related to how metatable indexing works:
Luau specializes method calls to improve their performance through a combination of compiler, VM and binding optimizations. Compiler emits a specialized instruction sequence when methods are called through obj:Method syntax (while this isn’t idiomatic anyway, you should avoid obj.Method(obj) ). When the object in question is a Lua table, VM performs some voodoo magic based on inline caching to try to quickly discover the implementation of this method through the metatable.
For this to be effective, it’s crucial that __index in a metatable points to a table directly. For performance reasons it’s strongly recommended to avoid __index functions as well as deep __index chains; an ideal object in Luau is a table with a metatable that points to itself through __index.
If you want private values without the overhead you can put a underscore in front of all private variables, and consider them private.
local Class = {}
Class.__index = Class
function Class.new()
local self = setmetatable({}, Class)
self.PublicValue = "test"
self._privateValue = 10
self._anotherPrivateValue = 5
return self
end
function Class:Destroy()
end
This is not truly private, but it’s what I use myself, and I’ve seen other programmers do it this way.
Well, I found a similar, yet different way to accomplish that. I find it a bit hacky.
I basically made a local dictionary, that stores private values of objects, with the key being the object itself.
Here’s my module:
-- In a module script
local Module = {}
Module.__index = Module --Return the module if a index that doesn't exist is inputted
local PrivateValues = {} --Your private object datas.
--That's basically a dictionary, and the expected key is your object.
function Module.new() --Constructor
local data = {} --Make a variable for the private data
data.PrivateValue = math.random(1,7) --Assign value from constructor.
local self = {} --Create a new object
setmetatable(self, Module) --Add the methods of the copy to it
PrivateValues[self] = data --Save the object's data
return self
end
function Module:GetPrivateValue() --Add a Getter to the object-specified private data.
return PrivateValues[self].PrivateValue --Return what was saved in the dictionary, with the current object as a key.
end
return Module
Main code:
local lib = require(script.ModuleScript)
local myObjects = {lib.new(), lib.new(), lib.new(), lib.new(), lib.new(), lib.new()}
for i = 1,#myObjects,1 do
print(myObjects[i]:GetPrivateValue())
end
The output:
I find it a bit funny that you can also print all private variables if you wanted to:
(I re-tested which is why there are different numbers)