Is this an ok way to code things?

Lately, I’ve been trying to use composition in my project, but I’m currently at a point where I feel like the code isn’t really feeling right and I’m not sure if the code is fine and I’m just overthinking or if it should actually be changed.

AFAIK composition is essentially a bunch of mini classes to make one big class so with that in mind I have a player class that is made of 3 things input, animation and skills.

function Player.new()
	local self = setmetatable({},Player)
	
	self.input = Input.new(self)
	self.animation = Animation.new(self)
	self.skills = Kioken.new(self)
	
	return self
end

What I like about this approach is I can easily change things. Currently skills is set to Kioken which is a character you can play as and each character has 4 skills, but if I wanted to use the skills of a different character I can just set skills to something else. I pass self when making the object because it allows me to access other parts of the player for example if skills needed to access animation it could just do player.animation.

function Kioken.new(player) 
	local self = setmetatable(BaseClass.new(player),Kioken)
	
	-- reference to the player class for quick access to things such as input and animation (ex: player.input or player.animation)
	self.player = player
	
	-- Our base class handles hitbox creation so we can just get it and then do custom logic for this specific class or just run the default method
	self:GetHitbox().OnHit:Connect(function(hit, humanoid)
		self:HitLanded(hit, humanoid)
	end)	
	
	-- load Kiokens animations
	player.animation:LoadAnimations(animationFolder)

	return self
end

So it seems like a good setup, but at the same time something feels off any advice?

Its hard to say without seeing more of your use case but in general I try to avoid doing object oriented anything in Lua. Lua’s way of doing it is sort of an afterthought and doesn’t provide any of the safety guarantees that other OOP languages do such as RAII or generic type safety. I would estimate you can do everything you’re doing now without involving metatables, which are only needed if you want shadowing and overriding behavior, which you don’t here.

1 Like

It depends on your use-case. If it works for your use-case there is nothing wrong with OOP and creating your own sort of classes this way.

It looks like your use of composition is correct, and it allows for flexibility in changing the behavior of your player class by swapping out the different components (input, animation, and skills) with other implementations.

One potential issue with your current implementation is that the player class is tightly coupled with the specific implementations of its components (Input, Animation, and Kioken). This means that if you want to change the player’s behavior, you have to create a new component class for each change you want to make, and then update the player class to use the new component.

One way to decouple the player class from its components is to use interfaces. An interface is a contract that specifies the methods and properties that a class must implement in order to be considered a valid implementation of the interface. By using interfaces, you can create a player class that depends on abstractions (i.e. interfaces) rather than concrete implementations of its components. This allows you to change the behavior of the player by providing it with a different implementation of an interface, without having to modify the player class itself.