2

Although this is a gamedev project, the question is about general OOP practices, so I believe it goes here. Here's the problem: (note: I will call any equivalent of real-world physical objects "entities" from now on; e.g. a rock in the game is an entity)

A specific class:

class Character
{
public/private CharacterInventory Inventory;
public/private CharacterStats Stats;
public/private CharacterEquipment Equipment;
}

Inventory functionality is self-contained in the CharacterInventory class. Same goes for stats. My aim is to make each character in my game a self-manageable entity. What do I mean by this? To put it plainly, I want to design the base Character class so that in the end I get an object that reacts in its entirety even when the smallest change occurs, in any of its components. To make things clearer, here's an example: when an item that changes the strength of the character gets equipped, the CharacterEquipment component of Character gets updated. At the same time, the Character will call the Stats object to change the strength attribute.

I've thought about doing this using events. Character subscribes to Equipment's OnEquip event, and in its handler, tells the Stats component do do its job. I believe this is an elegant enough solution; my issue however lies somewhere else.

I've got two ways in which I can update the character's inventory. First, by making the inventory (and any other component) private and providing a public method inside of Character, that does the work of calling the appropriate update method in the inventory. Like so:

Character chara = new Character();
chara.AddItemToInventory(SomeItem);

The 2nd way is by making the components public and accessing them like:

Character chara = new Character();
chara.Inventory.AddItem(SomeItem);

To me, both ways seem roughly equivalent in terms of readability. But the 2nd way seems like it's breaking the whole "self-manageable" idea. Basically, I'm invading the "private" space of the character. By going with the 2nd way, I can hook to the inventory events from some other place, but then again, this breaks the main idea even further. However, going the 1st way, I get the impression of writing duplicated code; even though it's not! I think the impression is given to me by the fact that Character.AddItemToInventory(...) and CharacterInventory.AddItem(...) share enough naming similarities to suggest duplicated code.

To be sincere, I'd go the first way, even though it seems to me like an extra layer of code. Then again, to make the character a self-manageable object, an extra layer of security is essential. But I'd like to hear from people more experienced than me. Are there any advantages to the 2nd way? Any severe disadvantages? I just can't get over the impression that I'm writing extra, unnecessary code.

4
  • If you go the 2nd way, how are the stats being properly updated if having an item creates a change in the stats? That would seem like the issue to my mind with the 2nd approach.
    – JB King
    Dec 29, 2012 at 22:03
  • The event from Equipment would still get fired. Character would still receive it and make the appropriate change.
    – user52800
    Dec 29, 2012 at 22:05
  • Another thing that the 1st way allows me to do is: remove the whole events between Character and its components logic. Character.EquipItem(...) could have { Equipment.Equip(Item); Stats.ApplyChange(Item.StatsChanges); } in its body, and Character would not have to wait for an event from Equipment to fire.
    – user52800
    Dec 29, 2012 at 22:08
  • But then, Character would manage all of its components, thus removing the whole "react to small changes in different parts" idea.
    – user52800
    Dec 29, 2012 at 22:09

2 Answers 2

1

Are there any advantages to the 2nd way?

Absolutely, you're not repeating yourself. AddToInventory is just going to call Inventory.Add anyways. Worse, it is going to also know enough of the item to have to dispatch to the different things that care about it.

A better approach is to create your stats class with input as to where it can find modifiers. In the inventory, in skills you might've picked, etc. When someone asks for the character's Strength, it can aggregate the modifiers to provide a result.

Abstracting that well is near-impossible. Program design tends to be thorny with games in that regard since so many things can impact others depending on context. Focus on getting something working good-enough; your goal after all isn't making a great design - it's getting a game done.

1
  • Thanks for the tip with the aggregation; I'll try to find a solution that's good enough, and if I succeed, I think I'll centralize all modifiers somewhere. This would be especially useful when saving game data. Without the aggregation, I'd have to store the updated stats as well as the effects on them. With aggregation, I'd only have to store the modifiers (effects). I went with the 2nd way.
    – user52800
    Dec 30, 2012 at 17:49
0

There is no problem making 2nd option "self-contained" too. Simply include callback/event in CharacterInventory that gets called/raised when item is added and subscribe to it within a character class. This way, you get same behavior as 1st option, you dont need to re-implement the public interface AND you keep the implementation details hidden.

I don't see how it breaks the idea of "self-contained". Especially if you use callback methods, instead of events. I think this might be better option.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.