Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: how to handle a LOT of fields and their encapsulation cleanly?

Let's say I am tasked with coding some kind of an RPG. This means that, for example, I'll want to track a Character GameCharacter and stats thereof, like intelligence, damage bonuses or hitpoints.

I'm positively scared that by the end of the project I may end up with handling with very a high number of fields - and for each I would have to make sure they follow a very similar set of constraint and behaviours (for example, I want them to be bounded between a min and a max; I want to be able to distinguish between a "base value" and a "temporary bonus"; I want to be able to increment and decrement both without going through a setters and getters). Suddenly, for every field I would need one (two?) getter and four setters and maybe a couple resetters too! Even for 10 fields that means a LOT of methods all alike, eek.

For DRYness I have started encapsulating the logic of messing with those stats in Field classes, so that I could write code such as intelligence.applyBonus(10) or hitpoints.get() (which takes care the value returned is in range), etc. I have even gone to such a length to create classes to group those fields together, but that's not the point right now.

Now, I hit in this problem while "plugging" Field into GameCharacter: most Java textbooks say that each class should have private fields with public getters and setters. That sounds good in theory, and I've already built a whole class around an int; however, the idea doesn't sound as solid when you find yourself calling a getter to get... a getter:

thisCharacter.getIntelligence().get() //eeek

I'd much rather access the field directly. Maybe it's my Python/VB [1] "background", but for me it is cleaner, clearer and more straightforward:

thisCharacter.intelligence.get()

The (theoretical) problem with public fields is that I'm giving up all control on it; for example at some other point in the codebase, by misfortune, the following might happen:

thisCharacter.intelligence = somethingThatReallyIsNull;

Sounds like a subtle bug... but... I mean, should I really worry about that? I for one never plan to assign the Field directly [2], I have documented in the Javadoc that this is not something that should be done, but still I am new here so I'm a bit torn.

So I would like to hear what your take on this topic. Are the advantages of encapsulation so massive that I should go ahead and have getter getters and setter getters and so on... or should I take encapsulation in healthy measures and leave the Field as a public field?


[1] Yes, I know. I've been trying to forget. But we've just recently seen also a bit of C# and man, aren't properties sweet. Oh well.

[2] except in Constructors! And a getter won't save me from a faulty constructor.

like image 615
badp Avatar asked Apr 20 '09 15:04

badp


6 Answers

Sounds like you're thinking in terms of if(player.dexterity > monster.dexterity) attacker = player. You need to think more like if(player.quickerThan(monster)) monster.suffersAttackFrom(player.getCurrentWeapon()). Don't mess around with bare stats, express your actual intention and then design your classes around what they're supposed to do. Stats are a cop-out anyway; what you really care about is whether a player can or cannot do some action, or their ability/capacity versus some reference. Think in terms of "is the player character strong enough" (player.canOpen(trapDoor)) instead of "does the character have at least 50 strength".

like image 82
TMN Avatar answered Nov 01 '22 01:11

TMN


My experience is that in situations where you need a lot of fields, the number, nature, naming, and types of fields are so flexible and likely to change throughout the lifetime of your project that you would likely need some sort of map instead of fields.

For example have an attribute map from keys to values.

Provide public calls for getting and setting the attributes, but don't let everybody use them (or make sure they don't). Instead, create classes to represent each attribute you are interested in, and that class provides all the functions for manipulating that attribute. For example, if you have Strength, you could have a "StrengthManipulation" class that is initialized to a specific Player object, and then provides getters, setters (All with appropriate validation and exceptions), and perhaps things like calculating strength with bonuses, etc.

One advantage of this is that you decouple the use of your attributes from your player class. So if you now add an Intelligence attribute, you don't have to deal and recompile everything that manipulates only strength.

As for accessing fields directly, it's a bad idea. When you access a field in VB (at least in old VBs), you usually call a property getter and setter and VB simply hides the () call for you. My view is that you have to adapt to the conventions of the language that you are using. In C, C++, Java and the like you have fields and you have methods. Calling a method should always have the () to make it clear that it is a call and other things may happen (e.g., you could get an exception). Either way, one of the benefits of Java is its more precise syntax and style.

VB to Java or C++ is like Texting to graduate school scientific writing.

BTW: Some usability research shows that it's better to not have parameters to constructors and rather construct and call all the setters if you need them.

like image 20
Uri Avatar answered Nov 01 '22 01:11

Uri


Steve Yegge had a very interesting (if lengthy) blog post that covered these issues: The Universal Design Pattern.

like image 34
John Topley Avatar answered Nov 01 '22 00:11

John Topley


To me it looks like 'thisCharacter' might well have an 'intelligence' object for dealing with intelligence behind the scenes, but I question whether that should be public at all. You should just expose thisCharacter.applyInt and thisCharacter.getInt instead of the object that deals with it. Dont expose your implementation like that.

like image 24
willcodejavaforfood Avatar answered Oct 31 '22 23:10

willcodejavaforfood


Keep your fields private! You never want to expose too much of your API. You can always make something that was private public, but not the other way round, in future releases.

Think as if you'll make that into the next MMORPG. You'd have a lot of scope for bugs, errors, and unnecessary evil. Ensure immutable properties are final.

Think of a DVD player, with its miniamilistic interface (play, stop, menu), and yet such technicality inside. You'll want to hide everything non-vital in your program.

like image 1
Humphrey Bogart Avatar answered Nov 01 '22 01:11

Humphrey Bogart


It sounds like your main complaint isn't so much with the abstraction of setter/getter methods, but with the language syntax for using them. Ie, you'd prefer something like C# style properties.

If this is the case, then the Java language has relatively little to offer you. Direct field access is fine, until you need to switch to a getter or setter, and then you will have some refactoring to do (possibly ok, if you control the whole codebase).

Of course, if the Java platform is a requirement, but the language isn't then there are other alternatives. Scala has a very nice property syntax, for example, along with lots of other features that could be useful for such a project. And best of all, it runs on the JVM, so you still get the same portability that you'd get by writing it in the Java language. :)

like image 1
jsight Avatar answered Nov 01 '22 00:11

jsight