绕过事件的模板化虚函数的方法
Way around templated virtual function for events
我基本上是在尝试实现一个通用的观察者模式。
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)
)。我知道我需要一些 refactoring/redesign of 类 但我不知道如何做。有什么方法可以解决这个问题?
不幸的是,正如您在编译时可能已经看到的那样,不允许使用虚函数模板。
由于您选择了虚函数,我猜您打算以某种方式使用多态性。如果是这种情况,您可以使用 inheritance 完美地使用 tempalteless 实现。唯一的限制是您的不同参与者继承自您的通用 classes。但由于可能的多重继承,他并不是一个强约束。
观察者可能看起来像(我为受影响的项目使用主题,但你可以使用第三个 class):
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;
};
使用 class 就像:
struct ConcreteObserverA : Observer {
void OnEvent(const EventHandle& h, Subject& affectedItem) override {
cout<<"A:"<<&affectedItem<<endl;
}
struct ConcreteSubjectSA : Subject { };
这里是live demo。
真的需要一个快速的解决方案,所以想到了这个。我意识到这非常骇人听闻,我可能正在破坏编程原则,应该将我的计算机扔出 window 以弥补甚至建议将此作为解决方案等的罪过,但它的确如此我想要的东西很容易理解,所以现在我会坚持下去,直到出现更好的东西。
基本上我只是将(可能是原始的)数据包装在一个对象中:
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);
}
}
};
我基本上是在尝试实现一个通用的观察者模式。
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)
)。我知道我需要一些 refactoring/redesign of 类 但我不知道如何做。有什么方法可以解决这个问题?
不幸的是,正如您在编译时可能已经看到的那样,不允许使用虚函数模板。
由于您选择了虚函数,我猜您打算以某种方式使用多态性。如果是这种情况,您可以使用 inheritance 完美地使用 tempalteless 实现。唯一的限制是您的不同参与者继承自您的通用 classes。但由于可能的多重继承,他并不是一个强约束。
观察者可能看起来像(我为受影响的项目使用主题,但你可以使用第三个 class):
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;
};
使用 class 就像:
struct ConcreteObserverA : Observer {
void OnEvent(const EventHandle& h, Subject& affectedItem) override {
cout<<"A:"<<&affectedItem<<endl;
}
struct ConcreteSubjectSA : Subject { };
这里是live demo。
真的需要一个快速的解决方案,所以想到了这个。我意识到这非常骇人听闻,我可能正在破坏编程原则,应该将我的计算机扔出 window 以弥补甚至建议将此作为解决方案等的罪过,但它的确如此我想要的东西很容易理解,所以现在我会坚持下去,直到出现更好的东西。
基本上我只是将(可能是原始的)数据包装在一个对象中:
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);
}
}
};