Strive for loosely coupled designs
I spent this weekend modifying the Model-View-Controller design pattern of the game engine. Up to this point, I had implemented a simple hack to bypass the Model part of the MVC and allow the Controller to communicate directly with game characters.
However, such implementation was wrong, and it was time to fix it. So, I connected the model to the controller and disconnected the controller from the characters. From now on, the model would receive controller input messages and send them directly to the characters.
However, when I did this, I realized that the Model didn't have a proper way to communicate with the character. My first instinct was to implement a "changeState()" function.
From a game perspective, this makes sense. When you are playing a game, and you press a button, it typically means you want to change the state of a character. For example, from a walking state to a running state.
The problem was deciding the argument type for the changeState() function.
At first, I decided to make the argument type a string. It worked but soon realized that it could lead to user errors. For example, typing the wrong state such as "walking" vs. "Walking."
Listing 1. changeState with String type argument
void changeState(string uState);
You should always strive to make APIs simple to use but hard to break. And the changeState() function required a uniform way to receive data.
So I decided to implement a new argument type for changeState(). I opted for using an enum type which looked like this:
Listing 2. Character State Enum
typedef enum{
//walking state
kWalking,
//running state
kRunning,
//jumping state
kJumping
}CharacterState;
Listing 3. changeState with Enum type argument
void changeState(CharacterState uState);
The enum would prevent user errors by forcing them to use the enum members.
However, this meant developers would need access to this enum definition. For example when they want to add more character's states. When you are developing an API, you want to prevent users from modifying the API.
So I needed a way to prevent user errors and decouple the changeState argument from the engine's API.
I needed time to think about this. So, I went to the gym. Did 100 push-ups, 30 pull-ups and while jumping the rope, the idea of using void pointers hit me.
Listing 4. changeState with void pointer type argument
void changeState(void* uState);
By making the changeState() argument a void pointer, the developer can create new data types. He can use an enum or structure to hold the characters states and pass them along the model and character class. Thus preventing user errors and keeping the engine decoupled from the characters' possible states.
I wanted to share this with you because this is one of the seven principles of Object Oriented Programming: Always Strive for loosely coupled designs between objects that interact.
Hope this helps