Too many components! (Or: “Component pattern overuse”)
Recently, me and ghRibacki have been going on about component-based development, as one of many resources we will use to allow us to develop ourselves a game engine powerful and generic enough to tackle new projects easily. This pattern has the advantage of being far better in terms of decoupling code and improving its readability and maintainability than deep object hierarchies. Its effectiveness has been proved by professionals, and seems to be in widespread use currently, with some very real-life examples around, like Unity3D’s GameObject. I, myself, have tasted this pattern’s far-reaching power while developing Bloodstream Battles, by incorporating a Movement component on my objects, which allowed me to do things I previously thought were only possible with multiple inheritance and a huge headache.
Everything is fine, the sun is bright, it is a good day in the land of a newly-learned design pattern! But wait! As you plow through the code, building decoupled and elegant components, you realize something: Your previously bloated-with-code class now looks more like it is bloated-with-components! Ah, the perils of overuse!
Now, instead of dealing iself with everything that, well, makes the GameObject be what it is, all it does is ask its components to do the heavy lifting. Sure, that means it is working at a much higher level of abstraction now, and that is, ideally, good for reuse, but I guess the unadressed issue, so far, is of the overuse of the component pattern.
I guess the first argument one would see here against the component pattern is that it “introduces layers of indirection, which affect performance negatively”. Well, I dare you, I doubl- I TRIPLE DARE YOU to make a whole game with as little indirection as possible. You can’t can you? YOU CAN’T! You would have to reinvent the wheel so many times in your adorable, gigantic, messy spaghetti code that you would give up before you even finished the movement part properly. Good luck inlining all that code, I’ll be here with my reusable modules, NOT wasting my time, thank you very much. It isn’t even that bad, the performance loss. Okay, you’ll save two processor cicles, but at the same time, you will probably miss those 40,000 little objects you’re instantiating at the middle of your mess, because you CAN’T DEBUG SPAGHETTI CODE PROPERLY.
Also, ever heard of “premature optimization is the root of all evil”? There’s a reason all the smart people say that twice a day to their fellow programmers. But that is a matter for another post.
So if it isn’t performance loss, what could it be? One could guess that it is a potential loss of information hiding. With a class’ behavior mostly settled inside it’s components, you could easily design, say, a GameObject class that requires a couple of components at instantiation. “Yay!”, you yell. “Now I can combine different behaviors by just passing in this or that component! Not longer I have to change the inside of that class”. Well, you got that right, but, at the same time, now the calling code MUST know the inner behavior of your class. Instead of creating a SwordEnemy, you’re now creating a GameObject that takes in a SwordEnemyRenderer, an AgressiveBehavior and a SwordEnemyPhysics as parameters. It’s not the end of the world, but did the calling code REALLY need to know all that? Sometimes that little detail can result in an acute case of TMI, from a fellow programmer’s point of view.
Expanding on that argument, you could even say that, if unproperly managed, it can reduce a lot the code readability (which, by the way, is the whole point of the pattern), and you know that if you do that, Erich Gamma and Kent Beck will come haunt your dreams. Okay, they won’t, but I’ll make sure to be there to show you the ugly side of your code, and trust me, you won’t like it.
All in all, there’s not really a big problem with using component pattern, but as with all design patters, please follow the general recommendation of only using it when you’ve actually found a real problem that needs fixing. Otherwise, you’ll just be writing code needlessly, and I’m pretty sure wasting resources is not what you’d like to be doing instead of making games. I know I wouldn’t.