程序有许多系统(类)。是否允许系统按类名调用其他人?

时间:2016-05-18 11:43:30

标签: c++ architecture typeid typeinfo

假设我有一个游戏引擎。

假设它包含类图形 GamePlay 物理系统。
(真实案例是20多个系统。)

所有这3个都来自系统

这是简单初始化的草稿。

main(){
    Game_Engine* engine = new Game_Engine();
    Graphic* sys1= new Graphic(engine); //set to System::engine_pointer
    GamePlay* sys2= new GamePlay(engine);
    Physics* sys3= new Physics(engine);
    engine->addSystem(sys1); //add to Game_Engine's hash map
    engine->addSystem(sys2);
    engine->addSystem(sys3);
}

然后,我想让所有系统都可以互相调用。 防爆。 图片可以调用 GamePlay

所以我将 addSystem()设计为: -

class Game_Engine {  
    std::unordered_map<std::type_index,Sys*> hashTable; 
    void addSystem (System* system){
        hashTable.add( std::type_index(typeid(*system)), system );
    }
    template <class SysXXX> SysXXX* getSystem(){
        return hashTable.get(std::type_index(typeid(SysXXX)) );
    }
}

结果是每个系统可以仅使用类名称相互调用: -

class Graphic : public System {
    void call_me_every_time_step(){
        engine_pointer->getSystem<GamePlay>()->... do something ;
    }
}

现在,它按照我的意愿行事,但

  1. 我听说typeid对性能不利。

  2. Game_Engine.h 现在必须#include所有 Graphic.h GamePlay.h Physics.h ,因此编译时间增加 (我试图不包括它们 - &gt; 3个派生系统的typeid将返回错误的结果。)

  3. 是否可以避免这些缺点?怎么样?
    还有其他缺点吗? 这首先是一个糟糕的设计吗?如果是这样,什么是好的设计? (因为我对C ++的经验非常有限。)

    编辑1:下面对gudok的答案的回复

    我为每个系统添加了一个获取/设置功能。

    但是,我意识到,当有更多系统时,管理变得更加困难,至少对我而言。

    我逃避了它并使用模板代码,如上所述。

    对于gudok的解决方案,单个系统将增加程序员的工作如下: -

    1. 在“GameEngine”
    2. 中添加字段声明
    3. 添加另一个函数以返回某个系统
    4. 重命名一个类时,例如通过使用自动重构工具“渲染”“图形”,我必须将getGraphics()重命名为getRender()(以使代码可读)
    5. 比较问题中的代码,单个系统只需1行。

      engine->addSystem(new Graphics(engine));
      

      它并非如此简单,特别是当大多数系统正在改变名称时,系统的数量不断增加。

      编辑2:对gudok增强答案的回应

      SystemHolder {T} 派生的 GameEngine 可以将每个系统的工作量减少到2个位置: -

      : public SystemHolder<Graphics>
      

      engine.addSystem<Graphics>(new Graphics());  
      
      但是,它仍然是2个地方 有问题的代码仅使用1个地方 因此,这还不够好,但感谢您的努力!

1 个答案:

答案 0 :(得分:1)

使用哈希映射和typeids而不是在GameEngine中分别存储每个系统的原因是什么?在语义上,所有这些系统都做不同的事情。我宁愿做以下事情:

class GameEngine {
  std::vector<System*> systems;

  Graphics* graphics;
  Gameplay* gameplay;
  Physics* physics;

  void setGraphics(Graphics* graphics) {
    this->graphics = graphics;
    this->systems.push_back(graphics);
  }
  Graphics* getGraphics() {
    return this->graphics;
  }

  ...
};

这个解决方案背后的想法是:

  1. 每个系统都不同于语义观点。当您从某个地方访问图形时,很可能您将使用特定于Graphics的函数,而不是所有System的通用函数。分别存储每个系统会消除typeid和不必要的类型转换的必要性。
  2. 如果您需要以统一的方式处理所有系统(例如,推进游戏时间),请使用systems字段:

    for (auto it = systems.begin(); it != systems.end(); it++) {
      it->tick();
    }
    
  3. 编辑这是增强的解决方案。您可以通过从GameEngine继承SystemHodler来添加新系统。根据需要,使用SystemgetSystem<T>方法获取和设置特定setSystem<T>的实例是统一的。

    #include <vector>
    
    class System {
    public:
      virtual ~System() {}
    };
    class Graphics : public System {};
    class Physics: public System {};
    
    template<typename T>
    class SystemHolder {
    public:
      T* getSystem() { return system; }
      void setSystem(T* system) { this->system = system; }
    
    private:
      T* system;
    };
    
    class GameEngine: public SystemHolder<Physics>, public SystemHolder<Graphics> {
    public:
      template<typename T>
      inline void addSystem(T* system) {
        systems.push_back(system);
        SystemHolder<T>::setSystem(system);
      }
    
      template<typename T>
      inline T* getSystem() {
        return SystemHolder<T>::getSystem();
      }
    
    private:
      std::vector<System*> systems;
    };
    
    int main(int argc, char* argv[]) {
      GameEngine engine;
      engine.addSystem<Physics>(new Physics());
      engine.addSystem<Graphics>(new Graphics());
    
      engine.getSystem<Physics>();
      engine.getSystem<Graphics>();
    }