Computer Programs Are Like a Cake

August 11, 2018 on dev

What is a program? A program is a big hunk of code which takes input and once finished returns an output. But what is this big hunk of code and how is it’s code organized?

Every good program is organized just like a cake, it has multiple layers on the inside glued together with icing and on the outside has thick icing to present itself.

Any experienced developer will tell you, organization matters above all else. Being able to find specific pieces of code, and use it without losing your mind is important. Or even knowing where to add new code so others can find it. Mind you others is usually yourself a month later.

Organization can’t entirely be explained here but the first step is learning the different layers, being able to identify low-level code to high-level code to business-level code. You must train your eye to spot the different layers from that create a mental model in your head.

Let’s Start With An Example

Using C#, see the example.

public class Monster {

  public int health;
  public Image health;
  public int attack;

  public Monster(int health, Image image, int attack) {
    // set vars
  }

  public void AttackTarget(Monster otherMonster) {
    otherMonster.health -= attack;

    if (otherMonter.health <= 0) {
      otherMonther.health = 0;
      otherMonster.image = Image.deadMonsterImage;
    }
  }

  public void Update() {
    // if close enough to monster
    // attack monster
  }

  public void Render() {
    // render image
  }

}

So we have a Monster, it has some attributes like health and attack. It also contains methods like AttackTarget and Render. Do you see anything wrong with this class? Well it has merged together multiple layers into one nasty mess.

  • Update, AttackTarget This is controller layer code. It implements the game logic for what happens when one monster attacks an other.
  • health, attack, image This is model layer code. It is just data about the monster, a model should not have an implementation.
  • Render This is view layer code. It renders what the monster looks like on screen. It does not belong in this class.

If you’re confused I just described the MVC Pattern. These are the most common layers in software development; view, code logic (controller) and data (model) should always be separated.

Now don’t think there are only three layers in a program, there are layers within layers. Inside a controller there can be small layers that control a certain portion of the game. More on that later.

So What?

Some will think, so what?, I don’t care if layers are blurred and more code is put into a class. Well you’re going to be in a world of hurt before you know it.

The Monster class above will certainly work in any game, but only in the gameplay portion, not in any other context. What if you wanted a monster directory screen that just displays all the monsters in the game. You would then need to create an instance of the Monster class and drag along everything irrelevant with it. You don’t need the Update method or care about it’s health, but because you didn’t listen now you do.

This is also referred to as Separation of Concerns.

So how should the code be corrected? I won’t dive into that but I wrote a previous blog post about it.

We Must Go Deeper

So I spoke about the high-level layers, the views, the controllers and the models. So now you must always consider which layer a piece of code goes into.

The Monster class would be considered high-level, it’s close to the core gameplay (attacking monsters) and therefore it’s purpose is distinctly defined.

Low-level code are building blocks that create higher-level code. If that makes any sense. It is distance from it’s business application and the lowest of code has no dependencies. Let’s see an example.

public class StatRange {

    public int max { get; set; }
    public int min { get; set; }
    public int value { get; set; }

    public StatRange(int min = 0, int max = 0) {
        this.max = max;
        this.min = min;
    }

    public void Reset() {
        value = max;
    }

    public float Percent {
        get { return (float)value / max; }
    }

    public void Clamp() {
        value = Mathf.Clamp(value, min, max);
    }

    public bool IsMin {
        get { return value == min; }
    }

    public bool IsMax {
        get { return value == max; }
    }
}

Ok so just looking at this. A person would have no idea what this is for. I took this from one of my games and is actually used for health bars and magic bars – this is the high-level application. But based on it’s design it can be used for anything that has a minimum, a maximum and a value.

healthBar = new StatRange(0, 100);
healthBar.Reset();

healthBar.value -= 199;
healthBar.Clamp();

See what just happened? After changing the value property, the next line will clamp the value to either the min or max if the value is out of bounds. The naive developer would build Clamp into the setter of value, so it will automatically gets called like so:

public int value {
    get { _value; }
    set { _value = value; Clamp(); }
}

This seems like a great idea but now is no longer low-level, it now has a mind of it’s own. StatsRange is supposed to be generic to allow higher-level code to build on top of it, and which includes deciding when to clamp value. This can feel like a small issue but building these decisions into a low-level class will push it higher up closer to the core application and possibly restrict it’s usage.

Conclusion

A game or any application is built upon many small pieces of low-level code. All this low-level code is then wrapped with more code to finely define it’s purpose. This process continues until the highest point is reached, the business-level, the core game logic, or the domain specific language. It is well defined but difficult to test because it is so finely integrated into the application.