当前位置: 首页 > 知识库问答 >
问题:

如何减少事件总线实现中的耦合

南宫云
2023-03-14

在我的应用程序中,我有几个模块不适合“is-a”或“has-a”关系,但仍然需要相互通信和传递数据。为了尝试松散地耦合这些模块,我实现了一个事件总线类,用于处理从“事件海报”到“事件侦听器”的消息传递。

如果类希望注册接收某些事件,它们可以实现< code>IEventListener。同样,如果需要将事件推到总线上,类可以调用< code > event bus::postEvent()。当调用< code>EventBus::update()时,EventBus处理预定消息的队列,并将它们路由到注册的侦听器。

EventBus.h

#pragma once

#include <queue>
#include <map>
#include <set>
#include <memory>


class IEvent
{
public:
    static enum EventType
    {
        EV_ENEMY_DIED,
        EV_ENEMY_SPAWNED,
        EV_GAME_OVER
    };

    virtual ~IEvent() {};
    virtual EventType getType() const = 0;
};


class IEventListener
{
public:
    virtual void handleEvent(IEvent * const e) = 0;
};


class EventBus
{
public:
    EventBus() {};
    ~EventBus() {};

    void update();
    void postEvent(std::unique_ptr<IEvent> &e);
    void registerListener(IEvent::EventType t, IEventListener *l);
    void removeListener(IEvent::EventType t, IEventListener *l);

private:
    std::queue<std::unique_ptr<IEvent>> m_eventBus;
    std::map<IEvent::EventType, std::set<IEventListener *>> m_routingTable;
};

事件总线.cpp

#include "EventBus.h"


using namespace std;


/**
 * Gives the EventBus a chance to dispatch and route events
 * Listener callbacks will be called from here
 */
void EventBus::update()
{
    while (!m_eventBus.empty())
    {
        // Get the next event (e_local now owns the on-heap event object)
        unique_ptr<IEvent> e_local(move(m_eventBus.front()));
        m_eventBus.pop();

        IEvent::EventType t = e_local->getType();
        auto it = m_routingTable.find(t);
        if (it != m_routingTable.end())
        {
            for (auto l : ((*it).second))
            {
                l->handleEvent(e_local.get());
            }
        }
    }
}

/**
 * Posts an event to the bus, for processing and dispatch later on
 * NB: The event bus will takes ownership of the on-heap event here
 */
void EventBus::postEvent(unique_ptr<IEvent> &e)
{
    // The EventBus now owns the object pointed to by e
    m_eventBus.push(unique_ptr<IEvent>(move(e)));
}

/**
 * Registers a listener against an event type
 */
void EventBus::registerListener(IEvent::EventType t, IEventListener *l)
{
    // Add this listener entry
    // If the routing table doesn't have an entry for t, std::map.operator[] will add one
    // If the listener is alredy registered std::set.insert() won't do anything
    m_routingTable[t].insert(l);
}

/**
 * Removes a listener from the event routing table
 */
void EventBus::removeListener(IEvent::EventType t, IEventListener *l)
{
    // Check if an entry for event t exists
    auto keyIterator = m_routingTable.find(t);
    if (keyIterator != m_routingTable.end())
    {
        // Remove the given listener if it exists in the set
        m_routingTable[t].erase(l);
    }
}

如您所见,在我当前的实现中,我为要传递的每种类型的事件创建具体的 IEvent 实现。我这样做是为了使每个事件都可以附加自定义数据(这是我的情况的要求)。不幸的是,这意味着我的 EventBus 系统必须了解系统的所有用户,从而增加了我的 EventBus 类与该类的用户之间的耦合。此外,IEvent 接口需要将所有事件类型的列表保存为枚举,这具有相同的问题(增加耦合)。

  1. 有没有一种方法可以修改这个实现,使EventBus完全通用(不需要知道EventBus的用户),但仍然允许我在每个事件中传递自定义数据?我研究了C11可变模板函数,但不知道如何在这种情况下使用它们
  2. 作为一个附带问题,我在这里是否正确使用了std::unique_ptr

共有1个答案

袁奇玮
2023-03-14

问题1“有没有办法修改这个实现,使EventBus完全通用”:

简短的回答,是的。

更长的回答:有很多html" target="_blank">方法可以实现这一点。这里描述一个:

事件的生产者和消费者都需要就类型/数据达成一致,但EventBus本身不需要知道。实现这一点的一种方法是使用<code>boost::signals2::signal

但是,这也是可以补救的。通过使事件类型<code>EventBus::postEvent()</code>将<code>std::function作为参数

boost::signals2::signal<int> signal;
...
eventbus.postEvent(boost::bind(signal, 42));
// note: we need to use boost::bind (not std::bind) for boost::signals to be happy

<code>EventBus</code>将看到<code>std::函数

问题2“我是否正确使用了< code>std::unique_ptr”:

几乎我将删除对<code>EventBus::postEvent</code>的引用,使其:

void EventBus::postEvent(std::unique_ptr<IEvent> e);

通过这样做,您可以强制调用者主动移动< code>std::unique_ptr

氯吡咯烷酮指南 R.32:

“使用unique_ptr参数表示函数拥有小部件的所有权”

 类似资料:
  • 假设方法和的责任密切相关 第一个例子: 如果 •和在class中定义(因此class具有高度的内聚性) •类别使用和类别使用 然后 •与和类耦合 •更改的签名只需要更改,而不需要更改 第二个例子: 如果 •在类中定义(因此与耦合) •用类定义(因此与耦合) 然后 •更改的签名只需要更改,而不需要更改 a) 据我所知,上一个例子中的类与第一个例子中的类并不耦合!还是我错过了什么? b) 据我所知,第

  • 让我们跳进 API 获取事件总线 你获取到事件总线的引用,如下所示: EventBus eb = vertx.eventBus(); 还有每个 Vert.x 实例事件总线的单个实例。 注册处理程序 这个最简单的方法来注册一个处理程序用consumer。下面是一个示例: EventBus eb = vertx.eventBus(); eb.consumer("news.uk.sport", mess

  • 注:本节未经校验,如有问题欢迎提issue 最初设想是为了提供一种向多个actor群发消息的方法,之后EventBus被一般化为一组实现一个简单接口的可组合的特质: /** * Attempts to register the subscriber to the specified Classifier * @return true if successful and false if not

  • 如何实现内部事件总线以在webflux spring堆栈中执行异步操作? 我想要一个服务来发出一个事件: 发布者服务不知道的另一个组件应该能够决定对该事件做出反应。 在MVC应用程序中,我将使用来发布事件()和在处理程序上()。 反应式堆栈中的等价物是什么? 我考虑的另一个选择是运行嵌入式消息服务,因为这应该意味着异步语义学。但是对于一个简单的场景来说,这感觉像是一个很大的开销。 我发现这些线 用

  • 问题内容: 我正在以下Java版本上运行单线程Java应用程序: 启用该选项。仍然在启动应用程序时,使用监视系统时看到多个线程正在启​​动。我想尽可能减少启动的进程数,因为我有一个用例,其中涉及运行该应用程序的多个实例,这将达到我正在运行的系统上允许的最大进程数的顶峰。上。除了可以用来减少启动线程数之外,是否还有其他jvm选项? 问题答案: 除了禁用并行或并发GC外,还有以下选项可减少JVM线程数

  • 我正在做一个项目,我有两个线程一起运行。这个项目是一个模拟银行,本质上,我们有一个存款线程和一个取款线程。我有一个存款线程经常运行的问题,导致银行账户的余额上升。(我希望现实生活中也有这个问题。)如何减少一个线程运行的时间? 这是我的主要课程: 以下是取款和存款线程类: 最后她是交易类: 我试图让线程在达到锁定条件之前进入睡眠状态,但不幸的是,它不起作用。