In Getter Eradicator, Martin Fowler argues that there are cases when using getters and setters is entirely appropriate. There is the argument that using getters violates encapsulation, but Fowler argues that you shouldn’t think about hiding data so much as design decisions. The real case for avoiding getters is to discourage procedural design in object-oriented languages. Pulling data out of an object to decide what to do is just procedural programming, whereas telling the object to do something and letting it figure out for itself how to do so would be more appropriate. He referenced a few other articles, among them Tell, Don’t Ask which describes the principle above.
It made for great reading, but it made me a bit concerned. I know and have known beyond a doubt that I must be doing something wrong; there is no way that I can claim 100% accuracy and correctness with the code I write. What I don’t know is what exactly would be wrong. I might write some code that seems perfectly fine to me but would make someone more experienced roll his/her eyes.
Now, if I write code, and it works, and I manage to finish whatever project I am working on, someone might argue, “Hey! Who cares how ‘correct’ it is? It works, so it is good enough!” If my goal was to just finish a project and be done with it, yes, I would agree. If it works, it is correct enough.
But one of my goals is to improve my ability to create, and that includes improving my coding as well as my designing skills. It isn’t enough to be able to get something working, although doing so would be a great first step. I want to be able to write great code and know it. It may take years of practice, and the Thousander Club will certainly help, but I want to design and write better code.
Of course, practice doesn’t help when there is no one to look over your work except yourself. I might be doing something that works well enough that I don’t realize that I’ve done it completely backwards. It may be the source of some trouble, but if I am not aware that the trouble is optional, how can I do anything about it?
In the past, I’ve asked other game programmers to look over my work and provide some feedback. It is definitely enlightening to get an analysis on a real project and not just a grade in class for writing a function. I’ve asked questions on forums and IRC, but it usually depends on who is around to answer and if they feel the question is good enough. Otherwise, I’m on my own.
I’m sure I am not the only lone wolf out there, so please let me know: how do you improve your own abilities when you’re basically the only one to provide a “peer review”?
9 replies on “How Can I Design Code Better?”
I would recommend not to focus to much on “perfect” code. The search for this holy grail prevents one to learn from experience. During development, you will find parts that are not as flexible as you would want, or cause more bugs than you can tolerate. Then you can find a solution, using development forums etc.
And working with other developers does work. I think reading others code will help if working with others is not an option.
For some time I’ve been downloading the source of game projects in varying states of maturity. Sometimes they can be enlightening, but other times they are exercises in frustration.
While I can expect documentation in code to be sparse, it can be like reading an old work of Shakespeare without notes on the left page to help you along. If you get it already, there is no problem, but if you are exposed to it for the first time, it can be a real chore to get through.
I suppose it is just a matter of experience. I’ll learn what works and what doesn’t. Five years from now, I’ll probably look back at my concerns today and laugh.
The lone wolf coder has a very big advantage in growing his programming skills over when working with others. Working in a shop does increase your skills, but it will eventually limit your skills growth rate. The pure weight of the huge projects and large teams slow your progress.
This all becomes incredibly obvious as you grow and you come across less and less code that is up to your own personal standards, let alone code that you can learn from.
So.. Don’t worry so much about working on your own.
btw. If you want to look at some very nice high-quality code (IMO) in an open project, take a look at antigrain.
I realized after reading through the website that I had already seen Anti-Grain before. Someone mentioned it months ago, although I don’t remember where. I’ll definitely look into the source.
Code design is tricky, and in my experience it’s easier to overdo, than to underdo (if that’s a word…). I’m leaning more and more towards the ‘just get it to work’ philosophy and I’m adjusting my toolset for that. I find that C++ code really needs a fair bit of code design, otherwise you get stuck pretty quickly. So these days, I do my game programming in Lua mostly, and that makes it much easier.
My advice to you would be: find the coding style that works best for you. The benefit you have as a lone wolf is that you don’t have to design your code for others to work with. If you like to structure your code in a certain way – even if others may frown on it – then go for it; you don’t need to defend your coding style to others. Think only of what you want from you code. When you’re alone, it pays to be egocentric. 🙂
People have mentioned Python as an easy-to-use game development language, and I will look into it once Oracle’s Eye is finished.
As for my C++ code, I suppose my fears aren’t very specific. Sometimes it is easy to look at my code and say, “Oh, no! What if I am doing it wrong?!?”
And such questions aren’t very helpful at all. I should make an effort to ask “Is there a way I can improve this code?” I think there will be less freaking out and more productivity as a result. B-)
In my experience, improvement in your coding skills is not incremental but comes in jumps. I can even remember exactly where I was when certain things just suddenly made sense to me. It really is an epiphany, and my code after that is almost magically more elegant.
So, you want to try and increase the chances you have for such epiphanies. I find that reading *good* open source code and listening to others explain something, even if it’s something I supposedly already know, really helps. Also, explaining it to others. You have to really understand something to teach it, and trying to figure out how best to teach it can help you internalize it.
For instance, my native language, so to speak, is PHP. I had been working on finding good tips on how to write a good modular, plugin architecture and come up dry, and my multiple own attempts hadn’t gotten very far. Then I stumbled across Drupal. Suddenly, after a week or two of on-and-off studying, (fortunately it’s a very well documented project), I realized how to do function-level polymorphism in PHP and how to use objects and arrays “properly”. Even when I’m not working on Drupal now, my own code follows similar patterns and is almost gleefully elegant in places. (You know you’re doing something write when you can look at it and go “wow, that’s so elegant” rather than “wow, that’s so cool.)
Also helpful is to mimicry. Find an API that you like, or think is well implemented. Then try to reimplement it yourself. It doesn’t have to be an exact copy, and you can look at the actual code at times to figure out how they handled some particularly thorny issue, but try to make it your own. It gives you a good target to shoot for, but a challenge to figure out how to make it elegant. You’ll probably end up with something that’s similar to but not identical to (in API or internals) what you were copying. If you get a great idea in the process, go ahead and do it. Even if the code you end up writing you never use, it’s still a great learning experience. (In my case, I had to write a new CMS at work, fast. What I ended up with is not Drupal, but is very Drupal-esque, and in some places I find it even more elegant than Drupal’s code after I’ve gone back and looked at it.)
The Ruby folks are big on “beautiful code”. I find that a waste of time. “Elegant code”, however, is something to shoot for. I define elegant code as being code that logically makes sense to read, accounts for all possibilities without funky if-then or switch statements, and just “feels clean”. When you write a routine that seems to “just work” in your mind, and does so in code, you just know it in your gut. Let yourself take the time to write such code, and you’ll find your quality improving over time.
Much of that is language-specific, of course. What makes something elegant in PHP (arrays, implode, and explode are AWESOME) is not what makes it elegant in Perl (if it can’t be done in a regex it’s not worth doing) is not what makes it elegant in Java (switch should never be used, and else-if is the enemy) is not what makes it elegant in C++ (uh, not really sure 🙂 )
Another refactoring guideline I use is what I call the Hawking Rule. Steven Hawking (at least I think it was him; it could have been Einstein 🙂 ) once noted that the most unlikely number in the universe is 2. Something can be unique, and occur only once. Or it could occur lots and lots of times. But for something to occur twice and only twice? Extremely unlikely. (In context, it means that either we’re the only intelligent species in the universe or there’s a crapload of them out there, but there won’t be just one more.)
In code, that means the first time you write something, go ahead and write it inline if it makes sense to. Then if you find yourself writing it again elsewhere, or something very similar, 99% chance it means you’re going to write it a 3rd, 4th, and 497th time. STOP RIGHT THERE, and refactor it into a general routine (function, object, method, library, whatever your language does with such things). The extra 30 minutes you spend over copying and pasting it “just one time” will save you days of debugging next month.
You know, I really should just put these long entries in my own blog and trackback to you. 🙂
“Pulling data out of an object to decide what to do is just procedural programming, whereas telling the object to do something and letting it figure out for itself how to do so would be more appropriate.”
Yikes! Please don’t take that as 100% true! If you design all your classes that way, you will find that some features become extremely difficult to implement in a game.
For example, if you have a Door and a Key object in your game, how do you use the key to unlock the door? Is it Key->unlockDoor( door ) or is it Door->unlock( Key ) ? What if this door is in a room in which all locks do not function? So, all unlock() functions must fail. How do you code that feature? Do you add another parameter to Door’s unlock() method? Do you keep adding parameters to all methods in all object classes whenever you want to add a new feature? Maintenance nightmare! Yes, it is good academic object-oriented design, but it can be bad game design.
I say keep some classes as data containers (nouns), and have other classes perform the actions (verbs). A game can be seen as a view of data. Merging the view of the data into the data itself is a bad idea, in my experience.
Thanks for your advice! I have some things to think about. B-)