For Veure, my text MMORPG, I found myself worrying about the Character class turning into a god object. At over 2,300 lines, 105 methods, and growing, it was getting hard to keep track of everything. I want my code to be clean and easy to follow. As a result, when new behavior arose, I started thinking of other places to put behavior, and wound up with abominations like this:

if ( $missions->has_misssion_for( $character, $npc ) ) { ... }

If you're not familiar with the terminology, NPC stands for "non player character" and, as you might guess, they're implemented via the same class as player characters. So what does the above code do? Does it mean that your character has a mission for an NPC? Does it mean that an NPC has a mission for your character? Does it mean that your missions have a mission for ... wait, what does that even mean? The last one, though, is the natural reading of the above.

All because I was trying to limit the size of the Character class.

But wait, this is an MMORPG. Your character drives everything. Your character moves. Your character goes on missions. Your character has to interact with their inventory. Your character has to do all sorts of things and artificially shoving those things into different classes just to avoid the "god object" makes for unclear code like the above. However, shoving all of that behavior into the Character class means it's getting huge and unreadable.

Until now.

I don't want to get too experimental with the programming of Veure. I want solid, proven concepts. However, I had hit a wall with the Character class when I was implementing missions. There was so much stuff going on that I needed a better organization and while trying to constrain missions to the smallest scope possible (you don't want special cases for mission code littering your entire code base), I found myself pushing a number of mission-related methods into the burgeoning Character class because, damn it, that's where they belonged:

if ( $npc->has_missions_for($character) ) { ... }

There is zero ambiguity in the above code, god objects be damned (er ...).

If you're familiar with the concept of a partial class, it's a way of splitting a class's implementation across several files. It's often used with auto-generated code so that the generator can write code to a partial class and it gets compiled into your class later. That's what I started implementing today, but using roles for it.

Roles, as you know, can let you safely share code across classes which are unrelated by inheritance. I didn't want to share my Character code with different classes, I wanted to decompose it into logically related components. Now, near the top of my Character class, you'll see the following:

with qw( Veure::PartialClass::Character::Inventory Veure::PartialClass::Character::Mission Veure::PartialClass::Character::Movement );

Each of those is implemented as a role, of course, but I call them PartialClass instead of Role because that's what they are. That's shed a number of methods from the Character class and about 600 lines of code. More will be handled later.

This has a number of interesting benefits. Aside from logically grouping related parts of classes (albeit in different files), I can now take advantage of the requires functionality of roles:

package Veure::PartialClass::Character::Inventory; use Moose::Role; requires qw( _get_schema character_items inc_bank );

Now, if I accidentally delete a required method in a disturbingly huge class, the class might fail to compile instead of me hoping my test coverage would catch it (yes, it would, but you get the point).

I should have the first pass of missions merged into the master branch of Veure soon. They're still a bit more complicated than I like, but the open-ended nature of them means that if I wanted to, I could approach the complexity of text adventures with them. Instead of "go kill 5 things", how about solve a mystery? My mission system would support that (though it would be a hell of a lot more work to write).