【问题标题】:Way around templated virtual function for events围绕事件的模板化虚拟功能的方式
【发布时间】:2015-08-26 18:00:30
【问题描述】:

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

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)) .我知道我需要对类进行一些重构/重新设计,但我坚持如何去做。有什么方法可以解决这个问题?

【问题讨论】:

  • stackoverflow.com/a/32081759/1774667 能解决您的问题吗?基本上,“主题”或“广播者”在其消息中被模板化。接收者也在消息上输入(在我的情况下,接收者只是可调用的对象,以便轻松使用 lambda,删除观察者包括销毁返回的shared_ptr&lt;void&gt; 而不是返回到广播者,因为我发现确保广播者寿命长的接收器是错误的主要来源)

标签: c++ templates observer-pattern virtual-functions


【解决方案1】:

不幸的是,正如您在编译时可能已经看到的那样,不允许有虚拟函数的模板。

由于您选择了虚函数,我猜您打算以某种方式使用多态性。如果是这种情况,您可以很好地使用无模板实现,使用继承。唯一的限制是您的不同参与者从您的通用类继承。但由于可能的多重继承,他的约束不是很强。

观察者可能看起来像(我已将 Subject 用于受影响的项目,但您可以使用第三类):

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

obervee 几乎保持不变:

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

使用类将是这样的:

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

这里是live demo

【讨论】:

  • 谢谢,我目前几乎使用继承,但问题是affectedItem 参数——我不想传入Subject 或第三类,因为它可能不是对象,它可能是 int 或 bool 或其他一些原始数据类型。以库存示例为例:如果我删除了索引 i 处的一个项目,作为 GUI/观察者,我想确切地知道 i 是什么。
【解决方案2】:

真的需要一个快速的解决方案,所以想出了这个。我意识到这是非常骇人听闻的,我可能正在破坏编程的原则,应该将我的计算机扔出窗外,以弥补甚至建议将其作为解决方案等的罪过,但它正是我想要的,并且很容易理解,所以现在我将继续使用它,直到出现更好的东西。

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

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);
        }
    }
};

【讨论】:

    猜你喜欢
    • 2015-01-04
    • 2012-10-08
    • 1970-01-01
    • 1970-01-01
    • 2010-10-19
    • 1970-01-01
    • 1970-01-01
    • 2020-09-12
    • 1970-01-01
    相关资源
    最近更新 更多