Hi! I’m currently learning about OOP, and I made these modules by myself. I wanted to ask if I wrote this in good practice, or if there are better ways of doing this. The code is working, when I call Light.new(object) in a script, it adds a Proximity Prompt to that part, which when triggered makes the part’s material neon.
I tried to go for a composition style rather than inheritance, thinking that a light has a proximity prompt. Don’t know if it was the best approach though.
What other things could I try to write using OOP to practice more?
Proximity module:
local Proximity = {}
Proximity.__index = Proximity
function Proximity.new(holdDuration)
local self = setmetatable({}, Proximity)
self.HoldDuration = holdDuration
self.ActionText = "Activate me!"
return self
end
function Proximity.Activate(object, changes, player)
for i, change in changes do
print(i,change)
object[i] = change
end
print(player.Name .. " triggered " .. object.Name .. " proximity prompt ")
end
return Proximity
Light module:
local Proximity = require(script.Parent.Proximity)
local Light = {}
Light.__indes = Light
local changes = {
Material = Enum.Material.Neon,
}
function Light.new(object)
local self = setmetatable({}, Light)
self.Prompt = Proximity.new(1)
local int = Instance.new("ProximityPrompt")
int.HoldDuration = self.Prompt.HoldDuration
int.ActionText = self.Prompt.ActionText
int.Parent = object
int.Triggered:Connect(function(player)
Proximity.Activate(object, changes, player)
end)
end
return Light
You’re on the right track with using composition, and I think it’s a good call for this kind of modular setup. Having Light use Proximity instead of inheriting from it makes sense since a light “has a” prompt rather than “is a” prompt.
A couple quick suggestions:
In the Light module you’re missing a return self at the end of Light.new so you won’t be able to access any properties later.
Also looks like there’s a typo: __indes should be __index.
Might be worth storing changes as part of the Light instance so you can configure it per light object.
If you want to practice you can try making a simple inventory system where each item has its own module and behavior. It’ll help you get more comfortable with composition vs inheritance and when to use which.
I don’t see that you created any methods based on any of the classes like Proximity and Light, so you need to show that you can take the new object from Proximity or Light, and actually call functions on it, like for example Light:Destroy() for example. With these code examples, you could just remove the __index lines entirely and just not have any OOP-ness.
Just doing that is enough for composition level OOP. No need to “practice”, just write code and test it.
If you don’t like inheritance, then you should use another type of OOP because metatable OOP is for inheritance. C style OOP could also work, or you may use closures if you are paranoid
Metatable OOP is good if you need to store a lot of methods but is very bad when it comes to index speed.
In light version you dont need any of OOP becouse you are not storing anythink also i think its a recursive
Also its generally better to set metatable in the end of a constructor if you are going for metatable OOP:
local function Module.new()
local self = {}
return setmetatable(self,Module)
end