类型对象

“通过创建一个类来支持新类型地灵活创建,其每个实例都代表一个不同的对象类型。”

例如基类是怪物,设计师告诉我们怪物的种类繁多,如“龙”、“巨魔”。

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

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

private:
 int health_; // Current health.
};

两个子类,龙和巨魔

class Dragon : public Monster
{
public:
 Dragon() : Monster(230) {}
 virtual const char* getAttack()
 {
  return "The dragon breathes fire!";
 }
};

class Troll : public Monster
{
public:
 Troll() : Monster(48) {}

 virtual const char* getAttack()
 {
  return "The troll clubs you!";
 }
};

这些是非常糟糕的,

  1. 收到设计师的邮件,要把巨魔的攻击力从18修改为52
  2. 查看并修改Troll.h
  3. 重新编译游戏
  4. 查看变化
  5. 回复邮件
  6. 重复上述步骤

很明显这是非常扯淡的。

目前是一种类型一个类。

所以能不能直接搞两个类就行了,两个类,无限的类型。

示例

// 种族
class Breed
{
public:
 Breed(int health, const char* attack)
 : health_(health),
  attack_(attack)
 {}

 int getHealth() { return health_; }
 const char* getAttack() { return attack_; }

private:
 int health_; // Starting health.


 const char* attack_;
};

它是一个包含两个数据字段的容器:初始生命值和攻击字符串,怪物如何使用它

class Monster
{
 friend class Breed;

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

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

 int health_; // Current health.

 Breed& breed_;
};

当我们构造一个怪物时,给它一个种族对象的引用,

class Breed
{
public:
    Monster* newMonster()
    {
        return new Monster(*this);
    }

    // Previous Breed code...
};

修改后,看起来像这样

Monster* monster = someBreed.newMonster();

通过继承共享数据

种族都有一个基种族

class Breed
{
public:
 Breed(Breed* parent, int health, 
    const char* attack)
 : parent_(parent),
  health_(health),
  attack_(attack)
 {}

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

private:
 Breed*    parent_;
 int      health_; // Starting health.


 const char* attack_;
};

当我们构造一个种族时,先为它传入一个基种族,我们可以传入NULL来表示它没有祖先。

Breed getHealth和getAttack的实现

int Breed::getHealth()
{
 // Override.

 if (health_ != 0 || parent_ == NULL) 
  {
  return health_;
  }
 // Inherit.

 return parent_->getHealth();
}

const char* Breed::getAttack()
{
 // Override.

 if (attack_ != NULL || parent_ == NULL) 
 {
  return attack_;
 }

 // Inherit.

 return parent_->getAttack();
}

这么写的好处是,即便在运行时修改了种类,去掉种类继承或去掉某个特性的继承,它仍能够正常运作。

还可以模仿继承

Breed(Breed* parent, int health, const char* attack)
: health_(health),
 attack_(attack)
{
 // Inherit non-overridden attributes.

  // 继承 parent的属性
 if (parent != NULL)
 {
  if (health == 0) health_ = parent->getHealth();

  if (attack == NULL)
  {
   attack_ = parent->getAttack();
  }
 }
}

一旦构造结束,我们就可以忘掉基类,因为它的属性已经被拷贝了下来,要访问一个种族的特性,现在只需返回它自身的字段。

int getHealth() { return health_; }
const char* getAttack() { return attack_; }

假设游戏引擎从JSON文件创建种族,数据示例如下

{
 "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"
 }
}

其中巨魔的基种族是 Troll, Throll Archer 和 Troll Wizard 都是派生种族。

两个派生类的生命值是0,这个值可从父类继承。设计师可以在 Troll 类中调整这个值,三个种族都会一起更新。

评价

怎么说呢,这种东西真的好吗,说实话真的不一定。想想很美好,但实际用起来还真不一定好。

况且使用场景也有限。