How do you make custom good class?

If you make your own plugin you are mostly interested in module scripts, but the most interesting are the classes, for example the Vector3 class, or the Instance class and so on. These are very well structured and readable, but only if you do them well. I have looked at some of them at Egomoose on Github (Example of a Vector2 class), but I still don’t understand much about them. So I ask here: How can I write a good, readable class?

1 Like

You’re looking for object orientated programming.

2 Likes

@Ukendio Yes, and I already know the basic syntax, but there are things I don’t understand, for example:


function mt.__index(v3, k)
	local props = ref[v3];
	local k = lower(sub(k, 1, 1)) .. sub(k, 2);
	
	if (k == "unit") then
		local x, y, z = props.x, props.y, props.z;
		local m = 1 / props.magnitude;
		return vector3.new(x*m, y*m, z*m);
	elseif (props[k]) then
		return props[k];
	elseif (vector3[k]) then
		return vector3[k];
	else
		error(k .. " is not a valid member of Vector3");
	end
end

function mt.__newindex(v3, k, v)
	error(k .. " cannot be assigned to");
end

function mt.__eq(a, b)
	if (not not ref[a] and not not ref[b]) then
		local a, b = ref[a], ref[b];
		return a.x == b.x and a.y == b.y and a.z == b.z;
	else
		return false;
	end
end

function mt.__unm(v3)
	local props = ref[v3];
	return vector3.new(-props.x, -props.y, -props.z);
end

function mt.__add(a, b)
	if (not not ref[a] and not not ref[b]) then
		local a, b = ref[a], ref[b];
		return vector3.new(a.x + b.x, a.y + b.y, a.z + b.z);
	elseif (not not ref[a]) then
		error("bad argument #2 to '?' (Vector3 expected, got " .. typeof(b) .. ")")
	elseif (not not ref[a]) then
		error("bad argument #1 to '?' (Vector3 expected, got " .. typeof(a) .. ")")
	end
end

function mt.__sub(a, b)
	if (not not ref[a] and not not ref[b]) then
		local a, b = ref[a], ref[b];
		return vector3.new(a.x - b.x, a.y - b.y, a.z - b.z);
	elseif (not not ref[a]) then
		error("bad argument #2 to '?' (Vector3 expected, got " .. typeof(b) .. ")")
	elseif (not not ref[a]) then
		error("bad argument #1 to '?' (Vector3 expected, got " .. typeof(a) .. ")")
	end
end

function mt.__mul(a, b)
	if (not not ref[a] and not not ref[b]) then
		local a, b = ref[a], ref[b];
		return vector3.new(a.x * b.x, a.y * b.y, a.z * b.z);
	elseif (not not ref[a] and typeof(b) == "number") then
		local a = ref[a];
		return vector3.new(a.x * b, a.y * b, a.z * b);
	elseif (not not ref[b] and typeof(a) == "number") then
		local b = ref[b];
		return vector3.new(a * b.x, a * b.y, a * b.z);
	else
		error("attempt to multiply a Vector3 with an incompatible value type or nil")
	end
end

function mt.__div(a, b)
	if (not not ref[a] and not not ref[b]) then
		local a, b = ref[a], ref[b];
		return vector3.new(a.x / b.x, a.y / b.y, a.z / b.z);
	elseif (not not ref[a] and typeof(b) == "number") then
		local a = ref[a];
		return vector3.new(a.x / b, a.y / b, a.z / b);
	elseif (not not ref[b] and typeof(a) == "number") then
		local b = ref[b];
		return vector3.new(a / b.x, a / b.y, a / b.z);
	else
		error("attempt to divide a Vector3 with an incompatible value type or nil")
	end
end

function mt.__tostring(v3)
	local prop = ref[v3];
	return format(rep("%s, ", 2) .. "%s", prop.x, prop.y, prop.z);
end

I know this are methamethods, but i dont know why @EgoMoose use it.

1 Like

The meta methods are what allow you to use, say, a * b , instead of having to do a:Multiply(b). So when the user writes a * b with your class, the.__mult(a,b) function is called.

The lua wiki explains these in further detail: lua-users wiki: Metamethods Tutorial

5 Likes

@madattak But I can also change the name of mt, or (I know how OOP works, I’m just afraid that the name has to be mt) Then I only have two questions: 1. What the methamethod __type does do in this case? 2. What the methamethod __mode does do in this case?

1 Like

mt can be replaced with whatever you want, it’s just a name, presumably short for metatable, as long as its the same as the name you gave to the metatable. Most of the methods can be seen here:

I assume type controls what happens when you use type(a), but not sure.

Mode is used for weak tables
https://www.lua.org/pil/17.html
(tl;dr objects held only by a weak table and nothing else will not be considered ‘in use’ by lua and garbage colected (erased and forgotten))

2 Likes

@madattak Why and when should I use weak values? What do I gain if I use them? The link did not help me there.

1 Like

I think it’s basically for very rare cases when you need a table full of objects (Not Roblox Instances like parts though!) and need them to be cleared out of memory without removing them from the table yourself. I’ve never needed to use them though, so someone else would probably be able to provide a better answer!

2 Likes

@madattak, sorry if i relive this topic (and if i am annoying you), but i just realized that i not understand why Egomoose use the ref table. It would be simpler by use the vector2 class self, or not?