There’s this old parable about several blind men and an elephant. There story goes something like this: Asked to describe an elephant each blind man grabs a part of the elephant then proceeds to describe what they felt. One feels the elephant’s tusk and describes an elephant as a hard, bony creature. Another feels it’s leg and describes the elephant as being stout like a tree trunk. Yet another grabs the elephants tail and promptly describes the elephant as being like a snake. Naturally, once they start sharing their findings, an argument ensues about what an elephant really is.
Each of these poor men is being totally honest in their description, but the end result ends up being divorced from reality by the limits of their own perception. The moral of the story is for one to be aware of where their limited perception (or preconceptions) may hide information from them. In more colloquial terms, it’s about learning to see the whole picture.
This lesson also applies to programming, particularly when designing code. For almost any programming problem, there are several, potentially competing, factors that play into its solutions. It’s easy to get so locked into a single mindset that you overlook important factors that you would only have noticed from a different vantage point.
When I need to design a piece of code, I try to think about it from at least the following three perspectives. Of course, these thee perspectives don’t represent the full set of potential viewpoints from which to examine a problem. These are just the three that I’ve found to be the most useful tools for my day-to-day programming work.
At this stage, I think about how I would structure my code with traditional Object-Oriented Programming techniques. I think about the data and functions that would be a part of the core “objects” in the feature I am creating. If things get hairy, I bust out some UML to track dependencies and ownership relations. This views tends to make the code most easily digestible for many game programmers because most of them have been raised on OOP principles.
This is where I look at the problem I need to solve in terms of data that needs to be processed. As opposed to thinking in terms of classes which are collections of data and functions that operate on that data, I think about data processing as a pipeline. In many cases, this sort of thought process leads me to the core update loops that will form the core of the feature I’m implementing. This view is often about making the code easily usable for the target machine. In many cases it’s about taking data that has been gathered and normalized so it can be processed in a minimally-complex fashion.
I wouldn’t be a very useful programmer if I ignored my end-users. If my feature needs someone to author data for it (and almost every feature does in some way or another), I’d better make sure they can create that data in a reliable and reasonably error-proof fashion. This is especially true if they have to produce a lot of data or need to iterate on that data repeatedly.
I don’t always use the above three views in that order, but I generally make sure to ask myself if I’ve considered my design from all three of these perspectives before I start working on any significantly large feature. Sometimes the best thing I can do when programming is to stop and ask myself: “Have I seen the whole elephant?”