Thoughts on data models, data structures, OOP, and other creatures of the Dark
When interviewing new programmers I often ask “How would you model Age in years for a Person?” At first blush most will usually respond with “an integer!” and they are correct, but not correct enough. Actually there is no wrong or right answer (event saying “I wouldn’t” because an Age may not be part of the requirements but lets leave that aside for the moment and assume that Age is required and you must model it).
Okay, so an integer:
age = 1How about negative ages? Can someone be -3 years old? Not usually. Maybe in Dr. Who but you would need a Tardis class as well. You need to wrap the integer in an object to handle negatives:
class Age: myAge = 1 def setAge(self, newAge): if newAge >= 0: self.myAge = newAge else: self.myAge = 0You now have an Age class which is an integer with special logic. You have modeled the concept of age in a very simplistic form.
The importance of wrapping the myAge in a class should not be understated. This has a lot of cascading effects in the rest of the program logic. For example, every Person has an Age so you can see if one Person is “older” than another. You write your logical statements around the concepts you are working with, not around integers, floats, or functions.
If we define Person thus:
class Person: myAge = Age() myName = ‘My name here!’we then have two data models: Age and Person. The only things we are about with a Person is its age and name so our model is fairly sparse but could be sufficient. The fact that Person (model) has an Age (another model) makes it a data structure, meaning that one model is dependent on another. Data structures usually end up being trees or hierarchies with some kind of weird mesh of interrelated objects thrown on top. Generally speaking, a well defined and well maintained data structure is essential to a quality product because it clearly represents what is being modeled and how individual components interact with one another. Logic becomes clear, data pumps in and out, rivers flow with wine.
What does this have to do with game development? Everything. And I would argue that good data structures and OO design matter more for game developers than for developers working in normal industry. Game developers are making up logic about made up stuff for people who don’t know the program logic. Unless you have a very VERY detailed design document your code will be the “right” answer because it is the only one. So you modeled Age as an int, stuffed it into a database as an uint, shipped it to the client as a hex, then displayed it as purple text. Six months later you get told “players are mad because their characters are dying of old age after only a month of game play!”. Which int is wrong? Was it a problem unpacking the UDP packet? Was it the database upgrade? Is it a display issue? Who knows, fire up the debugger and try to reproduce. My experience has been that when I cut corners and don’t try to push things to the data structure I end up paying for it. And usually don’t save any development time.
Good data structures don’t spring into existence after a whisky fueled brainstorming session, they evolve over time and through constant refactoring of the code. Very painfully they are expanded and then fixed, expanded and fixed. Stuff you thought you would need is superseded by a more elegant structure somewhere else and stuff you thought you wouldn't need anymore is re-added later. You know you have a good data structure when you bring a new developer on board and he or she looks at your structure and says “that's it? I could have done that in an hour!”. What you have left is the simple solution that works. But you won’t get there until you try many wrong paths.