如何处理派生类的多个重载而不需要对其他文件进行多次更改

Posted

技术标签:

【中文标题】如何处理派生类的多个重载而不需要对其他文件进行多次更改【英文标题】:How to handle multiple overloads for dervied classes without requiring multiple changes to other files 【发布时间】:2016-04-02 21:34:44 【问题描述】:

我不知道如何表达这个问题的标题,但这是我的问题:

我正在使用实体组件系统架构在 C++ 中开发游戏。在这种架构中,每个游戏对象都表示为一个类:

class Object

类只不过是组件的容器,它处理游戏对象的部分逻辑。

class Component

每个组件都有一个更新函数和特定事件的事件处理程序。每个事件都派生自一个公共基类,每个子对象都有自己的数据字段来描述事件。在我的实际代码中,基本 Event 类具有所有 Event 共享的一些属性。

class Event

;

class OnDeathEvent : public Event

public:
    int getKillerID() const
    
        return 92341;
    
;

class OnLevelEnterEvent : public Event

public:
    int getLevelEnteredID() const
    
        return 100;
    
;

在整个代码中,我们向对象发送事件。这是我为测试示例代码而编写的主要函数:

int main()

    Object obj;
    obj.sendEvent(OnLevelEnterEvent());
    obj.sendEvent(OnDeathEvent());
    std::cin.ignore();
    return 0;

当一个对象接收到一个事件时,它将对该事件的责任委托给它的组件。

    for (auto& component : components)
    
        component->handleEvent(e);
    

我想为每个事件类型使用函数重载。事件处理函数的目的就变得非常清楚。

void handleEvent(const OnLevelEnterEvent& e) 

但是这样做需要 Object 类和 Component 基类都声明每个 Event 类型的所有重载。显然,这是我们想要避免的事情,因为我们必须更改多个类才能将新的事件类型添加到我们的游戏中。以下是示例代码的其余部分:

class Component

public:

    virtual void handleEvent(const Event& e) 
    virtual void handleEvent(const OnDeathEvent& e) ;
    virtual void handleEvent(const OnLevelEnterEvent& e) ;
;

class TestComponent : public Component

public:
    virtual void handleEvent(const OnDeathEvent& e)
    
        std::cout << "You died. Your killer was: " << e.getKillerID() << std::endl;
    
;

class AnotherComponent : public Component

public:
    virtual void handleEvent(const OnLevelEnterEvent& e) 
    
        std::cout << "Level Entered with ID: " << e.getLevelEnteredID() << std::endl;
    
;

对象类:

class Object

public:
    Object() 
    
        components.push_back(new TestComponent());
        components.push_back(new AnotherComponent());
    


    void sendEvent(const Event& e)
    
        for (auto& component : components)
        
            component->handleEvent(e);
        
    

    void sendEvent(const OnDeathEvent& e)
    
        for (auto& component : components)
        
            component->handleEvent(e);
        
    

    void sendEvent(const OnLevelEnterEvent& e)
    
        for (auto& component : components)
        
            component->handleEvent(e);
        
    

private:
    std::vector<Component*> components;
;

我们希望能够通过从 Event 类继承来添加新的 Event 类型,而无需更改其他文件或创建新文件(新的 Event 类除外)。什么是构建代码的优雅方式?

欢迎提出任何建议。

【问题讨论】:

【参考方案1】:

您的组件可以注册该事件。 当子事件被调用时,它可以通知其注册的事件监听器。您可以定义一个接口并使用接口继承。

【讨论】:

我对您所说的内容有了一个很好的了解,但我不完全确定这在代码方面的表现如何。一个简短的例子将非常有帮助。感谢您的回答! 配合增强信号效果更好。【参考方案2】:
#include <iostream>
#include <vector>
#include <boost/signals2.hpp>
class Event

public:
    typedef boost::signals2::signal<void (Event&) > T_notify_signal;
    virtual ~Event()
    void Register(const T_notify_signal::slot_type& c) m_listeners.connect(c);
    void NotifyListeners() m_listeners(*this);
    virtual std::string info() const = 0;
private:
    T_notify_signal m_listeners;
;
class OnDeathEvent : public Event

public:
    virtual ~OnDeathEvent()
    virtual std::string info() const  return "you are dead"; 
;
class OnLevelEnterEvent : public Event

public:
    virtual ~OnLevelEnterEvent()
    virtual std::string info() const  return "you are in a new area"; 
;
//----------------------------------------
class Component

public:
    virtual ~Component()
;
class TestComponent : public Component

public:
    TestComponent()
    virtual ~TestComponent()
    virtual void notifyLevelEnter(Event& e) std::cout << "TestComponent::notifyLevelEnter(" << e.info() << ")" << std::endl;
;
class AnotherComponent : public Component

public:
    AnotherComponent()
    virtual ~AnotherComponent()
    virtual void notifyDeath(Event& e) std::cout << "AnotherComponent::notifyDeath(" << e.info() << ")" << std::endl;
    virtual void notifyLevelEnter(Event& e) std::cout << "AnotherComponent::notifyLevelEnter(" << e.info() << ")" << std::endl;
;
//----------------------------------------
class Object

public:
    Object()
    ~Object()
    void addComponent( Component& component ) m_components.push_back( &component );
    void sendEvent(Event& e) e.NotifyListeners();
private:
    std::vector< Component* > m_components;
;
//----------------------------------------
int main()

   Object obj;

    // create components
    TestComponent tc;
    AnotherComponent ac;

    // create events
    OnDeathEvent      death_event;
    OnLevelEnterEvent level_enter_event;

    // hook up events to components
    death_event.Register( boost::bind( &AnotherComponent::notifyDeath, &ac, _1 ) );

    level_enter_event.Register( boost::bind( &TestComponent::notifyLevelEnter, &tc, _1 ) );
    level_enter_event.Register( boost::bind( &AnotherComponent::notifyLevelEnter, &ac, _1 ) );

    // add components to object
    obj.addComponent( ac );
    obj.addComponent( tc );

    // process game loop
    while ( true )
    
        std::cout << "obj.sendEvent(level_enter)" << std::endl;
        obj.sendEvent(level_enter_event);

        std::cout << "obj.sendEvent(death)" << std::endl;
        obj.sendEvent(death_event);

        std::cout << "obj.sendEvent(level_enter)" << std::endl;
        obj.sendEvent(level_enter_event);

        std::cout << "obj.sendEvent(level_enter)" << std::endl;
        obj.sendEvent(level_enter_event);

        std::cout << "obj.sendEvent(death)" << std::endl;
        obj.sendEvent(death_event);
        break;
    

    return 0;

【讨论】:

【参考方案3】:

如果我理解正确,您需要一个 sendEvent 方法来发送任何通用的 Event 类型。在这种情况下,您可以创建一个sendEvent(const Event* _event) 方法,如下所示:

void sendEvent(const Event* _event) 
    for (auto& component : components) 
        component->handleEvent(_event);
    

如果你已经正确地实现了Event 的所有派生类,那么多态性将负责根据Event 的特定子类自动调用正确的handleEvent 方法(嗯,指向它)传递给sendEvent

【讨论】:

除非我遗漏了什么,否则这是行不通的。最终我需要放弃,因为并非所有事件类型都共享相同的事件接口。并且向下转换并不比我已经拥有的代码好多少

以上是关于如何处理派生类的多个重载而不需要对其他文件进行多次更改的主要内容,如果未能解决你的问题,请参考以下文章

Unity如何处理lua热重载

如何处理来自 WASAPI 流的声音数据块而不将它们写入文件?

jquery ajax请求时如果页面卡主 多次发出相同请求 如何处理

快应用快应用用户协议隐私政策内容中可以多次跳转,点击返回未能返回上一级页面,该如何处理?

如何处理其他类的navigationController

如何处理共享同一通道的多个 goroutine