API design

Keep your APIs simple short and clean

Keep your APIs simple so that anyone can use it.

Keep your APIs short so that anyone can remember it.

Keep your APIs clean so that anyone can understand it (without reading the full documentation).

Hope this is helpful

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.

Incorrect MVC pattern

Incorrect MVC pattern

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.

Correct MVC pattern

Correct MVC pattern

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

Try to design user-friendly APIs

One of the goals of an API developer is to make the API user-friendly. Your public API should be compact and intuitive. It should provide enough information to start development without the need to read the whole manual.

The game engine is the first API that I have ever developed. As I used the API in several demos, I realized that the API's naming convention was not user-friendly. It didn't inform the user when and where to use certain functions. For example, the Collision Response of a character depends on its inertia tensor, center of mass, etc. These properties should be set in the initialization routine. So instead of using the "set" keyword as shown below:

void setInertiaTensor();
void setCenterOfMass();

I hint the user to use these functions in the initialization routine by prefixing it with the "init" keyword:

void initInertiaTensor();
void initCenterOfMass();

Also, the API did not indicate whether a function enables a behavior or property. For example, Collision Detection is a behavior that can be enabled, paused and resumed. In contrast, a character's name is a property that is set to a value. So instead of using the keywords "set" and "disable" as shown below:

void setCollision();
void disableCollision();

I hint the user that the function enables, pauses or resumes a characters' behavior. I also append the suffix "Behavior" to each function as shown below:

void enableCollsionBehavior();
void pauseCollisionBehavior();
void resumeCollisionBehavior();

In contrast, all properties use the prefix "set".

void setModelName();

A good API design is akin to a good GUI design. It should be simple enough for anyone to start using it and hard enough for anyone to use it incorrectly. Do not discount the importance of a well designed API. It is as important as writing documentation and as your (app) source code itself.

So make an effort to review, simplify and improve your public API.