Whitespace
Whitespace is *very* important for the readability of your code. Without it, the text will look crowded, and will be harder to parse through the text. Here's some recommendation on whitespace.
- Add space after the comma of a field, parameter, operations, the start of a comment, or argument.
- Add a new line after every field, parameter, operation, or argument if the line would be too long. It’s also important to make your judgment if the line’s readability would be helped by splitting it into several more. I recommend you set the limit to be around 80 characters. However, the limit you set is really up to you to decide. I know people that use 160, 120, 90, etc.
Example
-- OK!
b = function(a, b, c)
return {
a + b,
(c + b) ^ 2
}
end
-- I THINK IT'S OK
c = { 1, 2, 3, 4 } -- Not too long and conise.
- Split your code into relevant chunks using newlines.
Example (With your code)
Note the addition of an empty line.
-- Static
self.name = name
self.displayOrder = displayOrder
self.callback = callback
-- Dynamic
self.reference = newproxy()
- Add new lines to functions.
- Have consistent whitespacing (and well, code style) overall.
- There’s more to find, do some research on this!
Initialize in `.new`!
There’s a problem, you have a self.reference
field, but it’s set properly after calling initialize. Meaning, .new
object doesn’t actually fully construct the object.
.new
is expected to have the object already initialized and working. So, either invoke initialize in your .new
constructor if you expect it to be overridden (this implies this is an abstract class that needs to be inherited from) or simply move the logic into .new
. In either case, you’ll need to create a new parameter, parent, from the .new
function.
local buttons = {}
buttons.__index = buttons --@colber2677
function buttons.new(name, displayOrder, callback, parent)
local self = setmetatable({}, buttons)
-- Static
self.name = name
self.displayOrder = displayOrder
self.callback = callback
local button = Instance.new("TextButton")
button.BackgroundColor3 = Color3.fromRGB(99, 99, 99)
button.LayoutOrder = self.displayOrder
button.Name = self.name
button.Size = UDim2.new(1,0,0,50)
button.Font = Enum.Font.Fantasy
button.Text = self.name
button.TextSize = 25
button.TextColor3 = Color3.new(1,1,1)
button.Parent = parent
self.reference = button
return self
end
Callbacks
Callbacks are good.
However, the way it has been done thus far, these two…
… aren’t the best way.
The reasons are two-fold:
- You want to be able to dynamically change the callback, by doing the above examples, you will force the field to be permanently set to one callback. Modifying that field will not change as intended.
- OK, you also want to be able to define your own behavior that may involve that same event. So, now you’re forced to make two connections for no good reason. Moreover, you may want to pass in special argument(s) to the callback (id of the button, the object itself, etc). This is impossible with the current setup.
And, in terms of how DarthFS did it (first example), it forces us to handle the disconnection and connection created. This should be handled by the object instead! We should handle the connection in .new
and disconnection I’ll get into later.
Besides that… what to do? We can make a separate function that can handle this for us. I’m going to assume that self.reference
is already initialized for ease. Read the last section for that.
function buttons.new(name, displayOrder, callback)
local self = setmetatable({},{ __index = buttons })
-- ... Skipping lines ...
self._mouseEvent = self.reference.MouseButton1Down:Connect(function(...)
-- We can now do something like...
-- self.activated = true
-- If desired...
self.callback(self, ...) -- Or, we don't pass self? We can do whatever we want.
end
return self
end
Cleaning up after yourself...
In the end, we all make messes. It can be actually painful to clean up messes. Or... completely impossible depending on how we have done everything. So, let's make it easier by defining a method to do exactly that!
function buttons:destroy()
-- If we're destroying the object the event is connected to, this isn't needed.
-- But, getting point across right now.
self._mouseEvent:Disconnect()
self.reference:Destroy()
-- And we're done cleaning up!
end
In the future, you’ll find that you’ll be cleaning up large messes. I recommend you take a look into maids to help with this.
Isn't this overkill in the end?
I agree with @colbert2677 here,
In the end, this isn’t really doing anything that a simple function couldn’t do. In fact, I bet you can pull your init function and modify some things.
local function createButton(parent, displayOrder, callback)
local button = Instance.new("TextButton")
button.BackgroundColor3 = Color3.fromRGB(99, 99, 99)
button.LayoutOrder = displayOrder
button.Name = name
button.Size = UDim2.new(1,0,0,50)
button.Font = Enum.Font.Fantasy
button.Text = name
button.TextSize = 25
button.TextColor3 = Color3.new(1,1,1)
button.Parent = parent
button.MouseButton1Down:Connect(callback) -- I have no special things to do.
return button
end
Just remember, use the right tools for the job at hand.
Some other ones...
- There should be a menu class as well, which should create the menu, and manage the menu and buttons.
- Merge your parameters into one dictionary.
function buttons.new(props) -- Shorthand for properties.
local self = setmetatable({},{__index = buttons})
--Static
self.name = props.name
self.displayOrder = props.displayOrder
self.callback = props.callback
-- ...
self.name = self.name or "defaultName"
self.displayOrder = self.displayOrder or 1
-- ...