First, Im not english native, and my english couldnt be 100% correct
Over the time I learnt and used OOP classes in roblox using metatables, it was always a pain in the head to create some kind of superclasses that would work almost like roblox superclasses, and also were easy to set it up.
It was relatively easy to inherit methods, but for properties that wasnt the case, I was copy-pasting the properties because I was lazy. that would work if you dont have that many classes, but if you have 50 weapons that inherits a movement and shooting behavior it would be a nightmare to add or rename a property.
So I just made a class setting that works like roblox superclasses
I created this post so people can use it, I can get/know feedback/bugs, and you can comment more ways to achieve this so it’s a win-win situation
First let’s create a superclass as I would do
This is the Class Module
local Superclass = require(Classes.Superclass) --This is the superclass that has "to inherit" methods
local Class = {}
Class.__index = Class
setmetatable(Class,Superclass) --Inheriting
function Class.new()
local self = setmetatable({}, Class)
self.Property = "This is supposed to be only in this class"
self.Superproperty = "This is supposed to be in the Superclass and inherit it to this class"
return self
end
function Class:Destroy()
--Clean up
end
return Class
This is the Superclass Module
local Superclass = {}
Superclass.__index = Superclass
function Superclass:Supermethod()
--This method is shared in the Class "Class" Module
end
return Superclass
So, we would like to inherit the property to the class.
My first thought was to just create also a Superclass.new()
constructor, clone the table, and merge it with the class table, but it is a little code-smelly, so we need to try something else.
My second thought was to create a InheritedTable and use the metamethods __index to search for it and __newindex to set it, but it messes with our current __index for the class Methods, so we need to change it.
Also a good addition I wanted is to be able to set a Superclass to another Superclass in a chained inheritance.
I’m gonna call it “Megaclass”,and why not, add a :IsA() method like roblox, that support those superclasses
This is what I did:
Class Module
--This is the Class Module
local Class = {}
Class.__index = function(self,Index)
if rawget(Class,Index) then --We use rawget to bypass metatables and avoid stackoverflow
return Class[Index] --Returning normal methods classes
elseif rawget(self,"_Inherited") and self._Inherited[Index] then --Checking the superclass
return self._Inherited[Index] --Returning the Inherited property
else
return nil
end
end
Class.__newindex = function(self,Index,Value) --Setting to Inherited properties
if self._Inherited and self._Inherited[Index] then
self._Inherited[Index] = Value --Setting values to the inherited table
else
rawset(self,Index,Value) --Setting Values to the table (normal table behavior)
end
end
Class.ClassName = "Class" --The ClassName name (Optional, used in :IsA)
Class.Superclass = require(Classes.Superclass) --Superclass (Change it to your superclass module)
--You can set Class.Superclass to nil or delete this line if you dont want a superclass
if Class.Superclass then setmetatable(Class,Class.Superclass) end --Inheriting Methods
function Class.new(...)
local self = setmetatable({}, Class)
if Class.Superclass and Class.Superclass.new then --if superclass then creating a new superclass class and setting the _Inherited table with it
self._Inherited = Class.Superclass.new(...)
end
self.Property = "Property" --This is a property for this class
return self
end
function Class.isA(ClassName) --isA function
if(Class.ClassName == ClassName)then
return true --Classname match return: yes
elseif(Class.Superclass)then --It has a superclass
return Class.Superclass.isA(ClassName) --Recursively calling Superclass.isA()
else
return false --Not match
end
end
function Class:Method() --This is a class method
print("This is a method:",self.Property)
end
function Class:PrintBoth() --Method for checking if it can read both properties using self
print("Property",self.Property)
print("Super",self.Superproperty)
end
function Class:IsA(ClassName) --:IsA() method inside of the class calling .isA function inside of the Class metatable
return self.isA(ClassName)
end
function Class:Destroy()
--Clean up
end
return Class
Superclass Module
--This is the superclass module. It uses the same base code as the other one
local Superclass = {}
Superclass.__index = function(self,Index)
if rawget(Superclass,Index) then
return Superclass[Index]
elseif rawget(self,"_Inherited") and self._Inherited[Index] then
return self._Inherited[Index]
else
return nil
end
end
Superclass.__newindex = function(self,Index,Value)
if self._Inherited and self._Inherited[Index] then
self._Inherited[Index] = Value
else
rawset(self,Index,Value)
end
end
Superclass.ClassName = "Superclass"
Superclass.Superclass = nil --We dont need to inherit another superclass (you could tho) so you can delete this line
if Superclass.Superclass then setmetatable(Superclass,Superclass.Superclass) end
function Superclass.new(...)
local self = setmetatable({}, Superclass)
if Superclass.Superclass and Superclass.Superclass.new then
self._Inherited = Superclass.Superclass.new(...)
end
--Here you can set more properties that can be inherited
self.Superproperty = "Superproperty" --This is a shared property for this superclass
return self
end
function Superclass.isA(ClassName) --This is recursively called
if(Superclass.ClassName == ClassName)then
return true
elseif(Superclass.Superclass)then
return Superclass.Superclass.isA(ClassName)
else
return false
end
end
function Superclass:Supermethod() --This is an inherited superclass Supermethod
print("This is a supermethod:",self.Superproperty) --printing inherited property
end
function Superclass:IsA(ClassName)
return self.isA(ClassName)
end
function Superclass:Destroy() --We dont need this
--Clean up
end
return Superclass
Let’s do some testing
local NewClass = require(script.Parent.Classes.Class).new()
print("Class Created")
--Reading properties testing
print(NewClass.Property)
print(NewClass.Superproperty)
--Methods testing
NewClass:Method()
NewClass:Supermethod()
NewClass:PrintBoth()
--Setting properties testing
NewClass.Property = "PropertyChanged"
NewClass.Superproperty = "SuperChanged"
--Reading again to check
NewClass:PrintBoth()
print(NewClass.Property)
print(NewClass.Superproperty)
--Testing :IsA()
print(NewClass:IsA("Class")) --true
print(NewClass:IsA("Superclass")) --true
print(NewClass:IsA("Foo")) --false
Here are the results:
So, It works but, I know what you are thinking, this looks like a mess, do I really need 30 lines only for setting it up? it will get even worse when I add new properties and methods.
Well…, technically yes but it doesnt need to look like a mess, you can delete any whitespace to make anything you dont need to change (or see) one line, or, to make it even better you can add tabulations so the functions dissapear to the side
Here’s how it ends up
Yes. It works the same. Trust me I tested it
Here you can get the compressed code for creating a class
Aditionally. Here you can get the VSC snippet which I personally think is the best way to use it (It’s used typing “aclass”)
That’s all, let me know what you think about it, if you use superclasses in your workflow, and if you have some feedback or a different option you use
Thanks for reading. Goodbye <33