绕过事件模板化虚函数

Way around templated virtual function for events

本文关键字:函数 事件      更新时间:2023-10-16

我基本上是在尝试实现一个通用的观察者模式。

class Observer {
public:
    template <class T>
    virtual void OnEvent(const EventHandle& h, T& affectedItem) = 0;
};
class Subject {
public:
    void AddObserver(Observer* o) {
        observers.Add(o);
    }
    void RemoveObserver(Observer* o) {
        observers.Remove(o);
    }
    template <class T>
    void Notify(const EventHandle& h, T& affectedItem) {
        for (Observer* o : observers) {
            o->OnEvent(h, affectedItem);
        }
    }
private:
    set<Observer*> observers;
};

Observer::OnEvent()中,我想获得在事件中受到影响的道具(假设我刚刚向库存中添加了一些东西,并且需要在GUI中反映添加的内容——我会调用Subject::Notify(itemAddedEvent, newItem))。我知道我需要重构/重新设计一些类,但我不知道怎么做。有什么方法可以解决这个问题呢?

不幸的是,正如您在编译时可能看到的那样,不允许为虚函数使用模板。

既然您选择了虚函数,我猜您打算以某种方式使用多态性。如果是这种情况,您完全可以使用使用继承的无模板实现。唯一的约束是不同的角色继承自泛型类。但由于可能存在多重继承,这并不是一个强约束。

观察者可能看起来像(我已经使用Subject作为受影响的项目,但你可以使用第三个类):

class Subject; 
class Observer {
public:
    virtual void OnEvent(const EventHandle& h, Subject& affectedItem) = 0;
    virtual ~Observer() {}  // virtual function ? => better foresee a virtual destructor
};

观察结果将保持几乎不变:

class Subject {
public:
    ...
    void Notify(const EventHandle& h, Subject& affectedItem) {
        for (Observer* o : observers) {
            o->OnEvent(h, affectedItem);
        }
    }
private:
    set<Observer*> observers;
};

using类就像:

struct ConcreteObserverA : Observer {
    void OnEvent(const EventHandle& h, Subject& affectedItem) override {
        cout<<"A:"<<&affectedItem<<endl;
    }
struct ConcreteSubjectSA : Subject { };

这里有一个现场演示。

真的需要一个快速的解决方案,所以想到了这个。我意识到这是可怕的黑客,我可能破坏了编程的原则,我应该把我的电脑扔出窗外,以弥补我甚至提出这个解决方案的罪过,等等,但它确实是我想要的,而且很容易理解,所以现在我要继续使用它,直到更好的东西出现。

基本上,我只是把(可能是原始的)数据包装在一个对象中:

struct ParamBase {};
template <class T>
struct ConcreteParam : ParamBase {
    T data;
    ConcreteParam(T t) : data(t) {}
};
class Observer {
public:
    virtual void OnEvent(const EventHandle& h, const ParamBase& affectedItem) = 0;
protected:
    template <class T>
    T getParamData(const ParamBase& p) {
        const ParamBase* pb = &p;
        return ((ConcreteParam<T>*)pb)->data;
    }
};
class Subject {
public:
    // Add/Remove functions stripped
    template <class T>
    void Notify(const EventHandle& h, T affectedItem) {
        for (Observer* o : observers) {
            o->OnEvent(h, ConcreteParam<T>(affectedItem));
        }
    }
};

使用例子:

class InventoryUI : public Observer {
public:
    virtual void OnEvent(const EventHandle& h, const ParamBase& affectedItem) {
        if (h == itemRemovedEvent) {
            int index = getParamData<int>(affectedItem);
            removeItemAt(index);
        }
        else if (h == itemAddedEvent) {
            Item* item = getParamData<Item*>(affectedItem);
            addItemToGrid(item);
        }
    }
};