Page dedicated to knowledge related to type object design pattern described by by Robert Nystrom in the game programming patterns..

My understanding

The idea is that an object represents different conceptual type.

For example, in the following code, we have a Monster interface and a Dragon class inheriting from Monster.

// monster interface
class Monster
{
public:
  virtual ~Monster() {}
  virtual const char* getAttack() = 0;

protected:
  Monster(int startingHealth)
  : health_(startingHealth)
  {}

private:
  int health_;
};
// Dragon inheriting from Monster
class Dragon : public Monster
{
public:
  Dragon() : Monster(230) {}

  virtual const char* getAttack()
  {
    return "The dragon breathes fire!";
  }
};

If suddenly:

  • I need to create thousand new Monster type, it will take time to recompile.
  • I do not know what type of Monster I need to handle.

The idea is moving from inheritance to type object by defining an object composition handling all the cases, from this: Subclass To this: Type object

// a breed type
class Breed
{
public:
  Breed(int health, const char* attack)
  : health_(health),
    attack_(attack)
  {}
  // instanciate once a breed called "Dragon" to define a dragon and give the reference
  // if you need to create a new type, you instanciate a new object
  // to create a monster, you call the breed with newMonster
  // similar to factory?
  Monster* newMonster() { return new Monster(*this); }
  int getHealth() { return health_; }
  const char* getAttack() { return attack_; }

private:
  int health_; // Starting health.
  const char* attack_;
};
class Monster
{
  // breed can access private fields
  friend class Breed;

public:
  const char* getAttack() { return breed_.getAttack(); }

private:
  // constructor is private
  Monster(Breed& breed)
  : health_(breed.getHealth()),
    breed_(breed)
  {}

  int health_;
  Breed& breed_;
};
// instanciate a new monster
Monster* monster = someBreed.newMonster();

Sharing data through inheritance

class Breed
{
public:
  // specify the parent on instanciation
  Breed(Breed* parent, int health, const char* attack)
  : parent_(parent),
    health_(health),
    attack_(attack)
  {}

  int         getHealth();
  const char* getAttack();

private:
  // breed has a parent
  Breed*      parent_;
  int         health_;
  const char* attack_;
};
// define the constructor
Breed(Breed* parent, int health, const char* attack)
: health_(health),
  attack_(attack)
{
  // inherit non-overridden attributes.
  if (parent != NULL)
  {
    if (health >= 0) health_ = parent->getHealth();
    if (attack == NULL) attack_ = parent->getAttack();
  }
}

You cade then have a JSON file defining different breed:. No need to recompile the code to change a breed, just changing the JSON file:

{
  "Troll": {
    "health": 25,
    "attack": "The troll hits you!"
  },
  "Troll Archer": {
    "parent": "Troll",
    "health": 0,
    "attack": "The troll archer fires an arrow!"
  },
  "Troll Wizard": {
    "parent": "Troll",
    "health": 0,
    "attack": "The troll wizard casts a spell on you!"
  }
}

Is the type object encapsulated or exposed?

In our sample implementation, Monster has a reference to a breed, but it doesn’t publicly expose it. Outside code can’t get directly at the monster’s breed. We can expose or encapsulate the breed, as following:

const char* Monster::getAttack()
{
  if (health_ < LOW_HEALTH)
  {
    return "The monster flails weakly.";
  }

  return breed_.getAttack();
}