• 《游戏编程模式》学习笔记(十二)类型对象 Type Object


    定义

    定义类型对象类和有类型的对象类。每个类型对象实例代表一种不同的逻辑类型。 每种有类型的对象保存对描述它类型的类型对象的引用。

    定义往往不是人能看懂的,我们需要例子才能够理解。

    举例

    假设你要为一款游戏制作一些怪物敌人。这些敌人有不同的血量及攻击模式。 你会怎么写?
    最容易想到的就是,哦,我先写一个Monster抽象基类,里边定义了基本的属性和抽象方法,然后让子类继承这个基类,来制造特定的敌人。就像这样

    class Monster
    {
    public:
      virtual ~Monster() {}
      virtual const char* getAttack() = 0;
    
    protected:
      Monster(int startingHealth)
      : health_(startingHealth)
      {}
    
    private:
      int health_; // 当前血值
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    那从类图上来看,就是这样的
    在这里插入图片描述

    那么问题来了,每次你要创造一个新的种类的敌人,即使这些敌人仅仅是名字不一样,其属性和行为都差不多,你都得写代码。 你需要新建一个类,然后继承Monster,再定义其血量以及攻击方法。你的时间都会花在写这几行代码上。如果策划要做几百个这样的敌人,你就得写几百个这种类。同时,如果策划要修改某些属性,比如health,你就得打开VS修改,然后重新编译…这实在是太浪费时间了。

    但我们如果使用类型对象模式,我们就可以这么做:
    在这里插入图片描述

    我们重构代码,让每个怪物有品种。 不是让每个品种继承Monster,我们现在有单一的Monster类和Breed类。

    Monster类就是怪物类,Breed类则作为引用保存在怪物类中。通过这个系统,游戏中的每个怪物都是Monster的实例。 Breed类包含了在不同品种怪物间分享的信息:开始血量和攻击字符串。

    代码就是这样:

    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_; // 初始血值
      const char* attack_;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    class Monster
    {
    public:
      Monster(Breed& breed)
      : health_(breed.getHealth()),
        breed_(breed)
      {}
    
      const char* getAttack()
      {
        return breed_.getAttack();
      }
    
    private:
      int    health_; // 当前血值
      Breed& breed_;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    通过这种方法,为了获得攻击字符串,一个怪兽可以调用它品种的方法进行attack。 Breed类本质上定义了一个怪物的类型,这就是为啥这个模式叫做类型对象。

    通过这种方法,我们就可以不需要打开VS重新写类来创造新类型了。我们只需要修改Breed所在的配置文件的数据,就可以轻松地创造出无数种新怪物。

    类型对象模式的本质就是,将部分的类型系统从硬编码的继承结构中拉出,放到可以在运行时定义的数据中去。

    总结
    我们回顾下定义,现在就非常直观了

    定义类型对象类(Breed)有类型的对象类(Monster)。每个类型对象实例代表一种不同的逻辑类型。 每种有类型的对象保存对描述它类型的类型对象的引用。

    实例相关的数据被存储在有类型对象的实例中,被同种类分享的数据或者行为存储在类型对象中。 引用同一类型对象的对象将会像同一类型一样运作。 这让我们在一组相同的对象间分享行为和数据,就像子类让我们做的那样,但没有固定的硬编码子类集合。

    原文链接:
    类型对象 · Behavioral Patterns · 游戏设计模式 (tkchu.me)

  • 相关阅读:
    2022 年度优秀开源技术
    Linux系统性能监测工具——网络/进程
    4、MySQL 多版本并发控制原理-数据可见性算法
    2022年9月1日:在 Visual Studio Code 中使用 Git 版本控制工具(未完成)
    济南ITSS的由来及内容
    数字增益和模拟增益理解和示例
    Java的JDBC编程
    Python基础笔记
    iPhone NFC 设置教程(门禁卡/公交卡/校园卡等等)
    CIP或者EtherNET/IP中的PATH是什么含义?
  • 原文地址:https://blog.csdn.net/Currybeefer/article/details/133690542