Hi, this is something of a direct response to sleitnick’s video on YouTube:
Fixing Lua OOP
In this video, he talks about a few problems that he personally seems to have with the way OOP works in Lua, and offers his own little solution that quite frankly leaves a lot to be desired in terms of versatility.
In particular, he had a problem with some apparent serialization issues involving sharing data structures using metatables.
I’ve personally never run into this issue, and was pretty confused when he brought this up. I mean, the whole point of metatables is to make universal, shareable data structures.
So what could be the problem?
I believe the issue is a pretty simple misunderstanding of something pretty basic when it comes to OOP.
Something that makes or breaks everything when it comes to creating shared classes.
Something that can both be composed of universal elements and have inherent local utility.
The self keyword.
I literally just took his final script, removed the default empty table, wrapped his function inside a metatable, replaced all variables with self, and it worked.
Just for fun, I added a call method that does exactly that: call the object.
No need to go through the whole PersonModule.GetFullName(Person) process. If Person has already been established as an object, you can simply use Person:GetFullName().
That’s literally the whole point of OOP and he negated it with his “solution” lmao
Here’s what I came up with:
- Module
export type self = {FirstName: string, LastName: string, Age: number}
local self = setmetatable(
{
GetFullName = function(self)
return `{self.FirstName} {self.LastName}`
end;
-- Issues with .functions v :functions and self v first argument?
-- define self AS the first argument and forget about it.
-- if you run PersonModule.GetFullName() with nothing, it will return "nil nil"
-- because there is no object associated with the function
-- Same with PersonModule.Age: as there is no object to associate Age with, Age will be nil
-- until a Person object is defined and provided an Age value
},
{
__call = function(self, Person: self) return setmetatable(Person or {}, self) end;
-- Person:GetFullName() = PersonModule.GetFullName(newPerson)
}
)
self.__index = self
return self
- ServerScript
local repStor = game:GetService("ReplicatedStorage")
local PersonModule = require(repStor.PersonModule)
local SendPerson = repStor.PersonEvent
local PersonInfo: PersonModule = {
FirstName = "John";
LastName = "Adams";
Age = 50
}
local Person = PersonModule(PersonInfo)
game:GetService("Players").PlayerAdded:Connect(function(player)
task.wait(2)
print("PERSON", Person)
SendPerson:FireClient(player, Person)
end)
- LocalScript
local repStor = game:GetService("ReplicatedStorage")
local PersonModule = require(repStor.PersonModule)
local ReceivePerson = repStor.PersonEvent
local newPerson
function OnReceivePerson(person)
print(PersonModule.GetFullName(person))
-- prints it out properly, no errors, as the metamethods were set properly.
-- as the metatable was wrapped and called properly, serialization is a non-issue
newPerson = PersonModule(person)
-- we can also assign the sent info as a new object on the client's side
end
ReceivePerson.OnServerEvent:Connect(OnReceivePerson)
task.spawn(function()
repeat task.wait() until newPerson
print(newPerson:GetFullName(), "is", newPerson.Age, "years old!")
-- this prints out "John Adams is 50 years old!"
-- no need to go through the module all over again to search out object info
-- to print out a fake object we wish to emulate, because...
-- the object exists as its own object.
-- I can't believe I had to say that.
end)
I’m pretty sure the code speaks for itself, y’all can have fun with it, I’m out
I totally didn’t write this on my phone and forget the OnServerEvent:Connect line in the LocalScript