Organising top-level functions in classes

I’ve been trying to learn and apply paradigms like OOP into my scripting to hopefully improve the readability and reliability of my code. The main issue with this is that in my classes, I end up being left with a long list of functions that are generally unrelated and seem to have little connection with each other, all existing at the top level of my code. My main two ways of dealing this would be having tables of functions at the top level (which ends up becoming another messy cluster), or instantiating functions inside other functions (largely unreadable and would probably create issues for performance).

Here’s an example of what I mean:

exampleClass.__index = exampleClass

local function doThisThing()
	print("Fizz")
end

local function doThatThing()
	print("Buzz")
end

local function doAllThings()
	doThisThing()
	doThatThing()
end

exampleClass.new = function()
	local thisInstance = setmetatable({},exampleClass)
	
	thisInstance.Foo = doThisThing()
	
	thisInstance.Bar = doThatThing()
	
	thisInstance.FooBar = doAllThings()
end

return exampleClass

Is there a better way of handling these functions? I also know that in the context of my example, doThisThing and doThatThing could have been anonymous functions, but in a real situation they would be lot larger and more complex.

You could use another module for those functions.

--module
local extraFuncs = {}

function extraFuncs.foo()
    print("foo")
end

function extraFuncs.bar()
    print("bar")
end

function extraFuncs:foobar()
    self:foo()
    self:bar()
end

return extraFuncs
--then just require it
local extraFuncs = require(script:FindFirstChild("ExtraFuncs"))

What I would do in your case is this

local mt = {}

local classMembers = {}

classMembers.SomeValue = "Hello there"

function classMembers:Foo()
   print("Fizz")
   print(self.SomeValue)
end

function classMembers:Bar()
   print("Buzz")
end

function classMembers:FooBar()
   self:Foo()
   self:Bar()
end

return {
   new = function()
   	local newMemberInstance = table.clone(classMembers)
   	local newInstance = setmetatable(newMemberInstance,mt)
   	return newInstance
   end,
}
   

You can use it like this

local exampleClass = require(script.ModuleScript)
local newClass = exampleClass.new()

newClass:Foo()
wait(1)
newClass:Bar()
wait(1)
newClass:FooBar()

Thanks, though what would happen if I have functions inside the methods Foo, Bar and FooBar, where, for example, I have some sort of large sorting algorithm that is specific to that script and is used inside one of those methods?

You can just define a local function and use it in any class function you want

like here

local function someSortingFunction(tableToSort,someArg)
       -- Do sorting here
       return tableToSort
end

local mt = {}

local classMembers = {}

classMembers.SomeValue = "Hello there"

function classMembers:Foo()
   print("Fizz")
   print(self.SomeValue)
end

function classMembers:Bar()
   print("Buzz")
end

function classMembers:FooBar()
   self:Foo()
   self:Bar()
   someSortingFunction({},"Great arg")
end

return {
   new = function()
   	local newMemberInstance = table.clone(classMembers)
   	local newInstance = setmetatable(newMemberInstance,mt)
   	return newInstance
   end,
}