Type Objects

Learn an alternative way to manage many different types of object, without heavily relying on classes
3D art of a character working in an office
Ryan McCombe
Published

In our introductory lessons, we discussed the concept of inheritance, and how we could use it to create many different types of monsters. That made intuitive sense and fit within the conceptual model of inheritance, where objects can exist in descriptive hierarchies. A Goblin Archer is a specific type of Goblin; a Goblin is a specific type of Monster.

class Monster {};
class Goblin : public Monster {};
class GoblinArcher : public Goblin {};

There are many other ways to solve this problem, however. An alternative approach is the concept of a type object. Here, our objects store their type as a variable.

// Our monster types are represented by Breed objects
class Breed {
public:
  int Level;
  // ...other variables
};

// Our monster objects reference their breed
class Monster {
  const Breed& Breed;
};

A conceptual way of thinking of this would be that our monster has a specific subtype, rather than it being a specific subtype. This pattern allows us to create any number of different "types" of monster, from only two classes.

Using Type Objects

There are two main ways to use this pattern. We can create Monsters as we would normally, passing a reference to the breed as the constructor:

Breed Goblin { 10 };
Monster MyGoblin { Goblin };

To enable this, our Monster class would need an appropriate constructor:

class Monster {
public:
  Monster(const Breed& Breed) :
   Level { Breed.Level },
   Breed { Breed }
  {
    // Additional initialisation here
  }
private:
  int Level;
  const Breed& Breed;
};

An alternative way to arrange our code might be to give our Breed objects the ability to create Monster objects:

Breed Goblin { 500 };
Monster MyGoblin { Goblin.Create() };

Either approach is valid - the relative advantages and disadvantages just depend on our specific requirements and preferences.

Benefits of this Design

This might seem like a strange way to achieve the same thing, but getting into a situation where we don't need to add a new class to create a new type of monster has many advantages.

When our different monster types are simply a collection of data, we are a step closer to being able to take them out of the code entirely. This ultimately allows us to build systems where our monster types can be defined at run time. This means we can do things like:

  • Update types very quickly - no recompile necessary
  • Let others update types without modifying code - eg, designers, players
  • Define our types elsewhere - for example, in external tools or APIs
  • In an online game, update types over the network - no patching necessary

Drawbacks of this Design

It's clear how this pattern allows us to initialise variables to different values based on our types. However, inheritance also gave us the ability to have different behaviours based on type. It's less obvious how we can do that using this pattern.

It is still possible to provide behaviors within the data. Some projects even build out the ability to have code injected at run time - programming languages such as LUA are commonly used for this purpose.

Another approach is to have the functions within the Monster class do different things based on values provided within the type data. For example, our type could include values representing personality traits. We might have a variable representing Aggression, and our AI functions would do different things based on that value. Then, if we wanted our monster to behave differently, we simply change that number.

However, despite these options being available, classic inheritance still gives us the most powerful and direct way of overriding behaviours to create different behaviours.

So Which Design Should I Use?

We've covered many different ways to achieve similar goals. We've seen classic inheritance, type objects, and component-based composition.

Each have their advantages and disadvantages. Good software design is all about choosing the correct tool for the situation at hand. That can often involve a combination of different patterns, trying to find the mix that gets the best results for the specific project we're working on.

Was this post useful?

3D art showing a progammer setting up a development environment

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access

This course includes:

  • 60 Lessons
  • Over 200 Quiz Questions
  • 95% Positive Reviews
  • Regularly Updated
  • Help and FAQ

Not Ready Yet?

Subscribe to this course to return to it later, and get updates on future changes

Get Started Now

Intro to C++ Programming

Starting from the fundamentals, become a C++ software engineer, step by step.

Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved