A Case for Trivial Setters and Getters

Published November 27, 2007
Advertisement
I'll probably get a photo of my office up soon. I have gotten some new hardware, and the place is quite impressive. It's unfortunate that PS3s don't stack, though. It would've saved me a lot of space.

A Case for Trivial Setters and Getters

I've historically been strongly opposed to writing trivial getters and setters. Trivial means that there's no data validation in either direction, nor is there any kind of intermediate work (computation, locks, whatever). It's just a direct read or write. The usual reason that people do this is because they've been told it's encapsulation and makes good object oriented code. I've already talked about this type of behavior once, when discussing information hiding. I've advised people against doing this sort of thing before, as have other people whose design intuition I tend to trust. As a result, I'm rather uncomfortable in breaking from this viewpoint, but I think there's a strong argument from the other side which shouldn't be ignored.

A typical piece of software nowadays involves a lot of state from a lot of objects being read and modified all the time. Games suffer from this particular problem quite a bit, due to the sheer amount of objects involved in your typical game world. Large inheritance hierarchies (especially deep ones) exacerbate the situation even further, by seriously obscuring all of the different available state and locations where state can be changed. So then, how do you tackle a data corruption bug that is caused by legal code? (That is, not caused by a buffer overrun, bad pointer, or other serious safety failure.) If somebody is handing you a technically legal but completely undesired value, how do you find it?

This is a very real problem, that comes up quite a bit. If your game is in meters and set on earth, you probably don't want gravity to be set to 1.10. In fact, you probably want it around 9.81. So if you find that your gravity HAS been set to 1.10, perhaps by accident while trying to set gravity scaling, you need to track that down. Since gravity is a fairly universal parameter, you can probably get away with a memory breakpoint. It'll be hideously slow, since gravity is probably accessed pretty often and you're adding a page fault to every read, but it will single out that bad write eventually. That won't work if you're not sure of the exact memory location of the desired member, though. In a situation with objects being created and destroyed, you may not know more than the class of the object you're after (and maybe its name, if you're lucky). If you're modifying that variable directly from all over the place, and you don't know exactly where it is, tracking down the point where the corruption occurs will probably be a hellish exercise.

Then major advantage of a trivial setter or getter, then, is that you can inject a breakpoint, or even other code to help catch the bogus value you're looking for. You can do it at source level instead of messing with memory, and it won't drastically affect application performance the way a memory breakpoint does. The optimizer should inline the functions in your final build, so you lose nothing when speed actually matters. It's now a lot easier to debug where things are going awry, and all it takes is a couple extra minor lines of code per member. Compared to the overhead introduced by your typical C++ reflection framework, that's nothing at all.

C# makes this a lot easier by allowing transparent refactoring from direct access to properties, which give you all of the benefits without affecting client code. Unfortunately C++ doesn't, and since the people behind C++ have generally come down against properties, it probably never will. (This paper has some rationale. It's not unreasonable, but it is blind to several advantages of properties and the presented solution is a gigantic pain.) If you want to switch from direct access to a setter/getter pair in C++, the refactoring effort involved is potentially quite large, and equally so in the reverse direction if you wanted to make the change temporarily.

I'm still not fully sold on the trivial setters and getters, but the argument for them is very compelling as soon as you start dealing with any important data object that is used a lot. At work, we're supposed to favor the setters and getters for everything for just this reason, and there are frequently asserts in both functions. Those asserts and trivial functions are really rather useful in day-to-day bug fixing, and so I'm nearly ready to change my tune on this one.
0 likes 5 comments

Comments

Anon Mike
Memory breakpoints slowing things down is why god invented hardware breakpoints. I don't know if VS supports them but the Windows platform SDK debuggers do (via the "ba" command), no clue at all about gdb. Set a write breakpoint on your value and you're off and running at full speed until such time as the write happens. Also handy for tracking down all sorts of buffer overwrite issues.

In the case that you can't use hardware breakpoints it's not that hard to convert a C++ POD type to an object with appropriate operators and conversion functions that you can set breakpoints on. It's nowhere near as easy as C# but it can be done if all else fails.
November 27, 2007 05:14 PM
JTippetts
I routinely implement trivial get/set functions in classes if I intend to export them to Lua. I use tolua++, which will look for the functions if named a certain way, and map them to table index methods in the Lua object so that the associated members can be accessed directly in Lua script. Other than that, though, I never use them. Exposing a class's members usually feels like an act of rape. [grin]
November 27, 2007 07:45 PM
benryves
I tend to use trivial properties for a range of reasons, starting from the simple Microsoft tell you not to expose public fields, to support for the PropertyGrid control, to ease of updating the property to something more complex at a later date if I need to without breaking code that uses the class library at a referenced assembly level (rather than at the source level).

C#3 makes these trivial properties much easier to declare (public int SomeProperty { get; set; }), thankfully.

The only disadvantages I see of properties is that they're more time-consuming to declare (not an issue with C#3), you cannot pass them as ref or out parameters and accessing arrays via properties can have some rather strange side-effects (for example, changing the palette of images requires that you copy the array, make the changes, then copy the array back to the property).
November 28, 2007 09:02 AM
MrEvil
Quote: Original post by benryves
I tend to use trivial properties for a range of reasons, starting from the simple Microsoft tell you not to expose public fields, to support for the PropertyGrid control, to ease of updating the property to something more complex at a later date if I need to without breaking code that uses the class library at a referenced assembly level (rather than at the source level)


Something I discovered when doing localisation is that you can use a TypeDescriptionProvider/CustomTypeDescriptor to expose public fields as properties (or even add properties which don't exist, provide custom Reset() functions, etc.), if you want to do it that way. I used this to provide localised property names and descriptions.

Of course, it's easier just to use properties.
December 04, 2007 11:01 AM
c2_0
I used to be of the mind that using trivial getters and setters is simply redundant typing. I've later come to see some advantages to writing them, in particular for library code. In addition to those you mentioned (code insertion, debugging, profiling, tracing, etc):

Platform independent code
Getters and setters provide an interface to your object which can be kept consistent across multiple platforms. There might for example be platforms where your object only acts as a proxy and doesn't hold some piece of data itself.

Versioning
You can refactor or for other reasons change your implementations while preserving backwards compatibility and behavior.

The bottom line is it can make your code more future proof. This is probably only a valid argument for a small fraction of code written, but worth mentioning.
January 08, 2008 04:14 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement