Java 在C+中实现观察者模式+;

Java 在C+中实现观察者模式+;,java,c++,objective-c,design-patterns,observer-pattern,Java,C++,Objective C,Design Patterns,Observer Pattern,观察者模式在事件驱动系统中非常有用。以下是如何用两种语言实现它: Java 使用AOP库或字节码工程(BCEL、cglib、asm等)动态创建子类。对观察到的属性的getter或setter的任何调用都会通知任何附加的观察者 目标-C 这类似于Java—使用isa swizzling动态创建子类。对观察到的属性的任何调用都会通知连接的观察者。有趣的是,在Objective-C中,如果移除所有观察者,我们可以在不使用包装属性方法的情况下快速返回到原始类。而在Java中,类通常只加载一次,所以您总是

观察者模式在事件驱动系统中非常有用。以下是如何用两种语言实现它:

Java

使用AOP库或字节码工程(BCEL、cglib、asm等)动态创建子类。对观察到的属性的getter或setter的任何调用都会通知任何附加的观察者

目标-C

这类似于Java—使用isa swizzling动态创建子类。对观察到的属性的任何调用都会通知连接的观察者。有趣的是,在Objective-C中,如果移除所有观察者,我们可以在不使用包装属性方法的情况下快速返回到原始类。而在Java中,类通常只加载一次,所以您总是通知一组观察者(可能是空的)

< >强> C++如何?< /强> < /P>
<>在C++中有限的反射,使用上述方法是困难的。C++中的“最佳”(我指的是典型的或事实上的标准)是什么?有没有办法避免像我上面提到的Java和Objective-C实现中那样的锅炉板代码?也许使用C++元编程特性?

< p>我不相信有一种方法可以用反射来实现C++中的观察者模式。如果不使用任何外部工具,则必须手动实现所有功能。例如,我将实现如下内容:

#include <iostream>
#include <set>
using namespace std;

class Impl;

class ObserverBase {
public:
    virtual void propertyChanged(Impl *impl, int value) = 0;
};

class Impl {
public:
    void setProperty(int value) {
        if (m_property != value) {
            m_property = value;
            for(auto observer:m_observers) {
                observer->propertyChanged(this, value);
            }
        }
    }
    int getProperty() {
        return m_property;
    }

    void addObserver(ObserverBase *observer) {
        m_observers.insert(observer);
    }
private:
    int m_property;
    set<ObserverBase *> m_observers;
};

class Observer : public ObserverBase {
public:
    virtual void propertyChanged(Impl *impl, int value) {
        cout << "Saw new value of " << value << "!" << endl;
    }
};

int main() {
    Impl impl;
    impl.addObserver(new Observer());
    impl.setProperty(5);
}
#包括
#包括
使用名称空间std;
类Impl;
类ObserverBase{
公众:
虚空属性更改(Impl*Impl,int值)=0;
};
类Impl{
公众:
void setProperty(int值){
如果(m_属性!=值){
m_属性=值;
用于(自动观察者:m_观察者){
观察者->属性更改(此,值);
}
}
}
int getProperty(){
归还m_财产;
}
void addObserver(ObserverBase*observer){
m_观察员。插入(观察员);
}
私人:
国际货币基金组织财产;
设置m_观察员;
};
类观察者:公共观察者库{
公众:
虚拟void属性已更改(Impl*Impl,int值){

CUT< P>我不相信有一种方法可以用反射来实现C++中的观察者模式。如果你不使用任何外部工具,你必须手动实现所有的东西。例如,我会实现如下:
#include <iostream>
#include <set>
using namespace std;

class Impl;

class ObserverBase {
public:
    virtual void propertyChanged(Impl *impl, int value) = 0;
};

class Impl {
public:
    void setProperty(int value) {
        if (m_property != value) {
            m_property = value;
            for(auto observer:m_observers) {
                observer->propertyChanged(this, value);
            }
        }
    }
    int getProperty() {
        return m_property;
    }

    void addObserver(ObserverBase *observer) {
        m_observers.insert(observer);
    }
private:
    int m_property;
    set<ObserverBase *> m_observers;
};

class Observer : public ObserverBase {
public:
    virtual void propertyChanged(Impl *impl, int value) {
        cout << "Saw new value of " << value << "!" << endl;
    }
};

int main() {
    Impl impl;
    impl.addObserver(new Observer());
    impl.setProperty(5);
}
#包括
#包括
使用名称空间std;
类Impl;
类ObserverBase{
公众:
虚空属性更改(Impl*Impl,int值)=0;
};
类Impl{
公众:
void setProperty(int值){
如果(m_属性!=值){
m_属性=值;
用于(自动观察者:m_观察者){
观察者->属性更改(此,值);
}
}
}
int getProperty(){
归还m_财产;
}
void addObserver(ObserverBase*observer){
m_观察员。插入(观察员);
}
私人:
国际货币基金组织财产;
设置m_观察员;
};
类观察者:公共观察者库{
公众:
虚拟void属性已更改(Impl*Impl,int值){

CUT< P>我编写了大量C++代码,需要为我正在开发的游戏组件创建一个观察者。我需要一些东西来分发“开始帧”、“用户输入”等,作为游戏中有关事件的相关方。

<>我也希望它是直的C++,不依赖于平台或特定的技术(比如Boost、Qt等),因为我经常在不同的项目中构建和重用组件(以及它们背后的想法)。 以下是我提出的解决方案的大致草图:

  • 观察器是一个单例,它有键(枚举值,而不是字符串)供主题注册感兴趣的对象。因为它是单例,所以它总是存在的
  • 每个主题都是从一个公共基类派生的。基类有一个抽象的虚拟函数Notify(…),必须在派生类中实现,还有一个析构函数,当它被删除时,它会从观察者(它总是可以到达)中移除它
  • 在观察者本身内部,如果在进行通知(…)时调用Detach(…),则任何分离的主题都会出现在列表中
  • 在观察者上调用Notify(…)时,它会创建主题列表的临时副本。当它迭代主题列表时,会将其与最近分离的主题列表进行比较。如果目标不在其中,则会在目标上调用Notify(…)。否则,将跳过该主题列表
  • Observer中的Notify(…)还跟踪处理级联调用的深度(A通知B、C、D,D.Notify(…)触发对E的Notify(…)调用,等等)
  • 这就是界面最终的样子:

    /* 
     The Notifier is a singleton implementation of the Subject/Observer design
     pattern.  Any class/instance which wishes to participate as an observer
     of an event can derive from the Notified base class and register itself
     with the Notiifer for enumerated events.
    
     Notifier derived classes MUST implement the notify function, which has 
     a prototype of:
    
     void Notify(const NOTIFIED_EVENT_TYPE_T& event)
    
     This is a data object passed from the Notifier class.  The structure 
     passed has a void* in it.  There is no illusion of type safety here 
     and it is the responsibility of the user to ensure it is cast properly.
     In most cases, it will be "NULL".
    
     Classes derived from Notified do not need to deregister (though it may 
     be a good idea to do so) as the base class destrctor will attempt to
     remove itself from the Notifier system automatically.
    
     The event type is an enumeration and not a string as it is in many 
     "generic" notification systems.  In practical use, this is for a closed
     application where the messages will be known at compile time.  This allows
     us to increase the speed of the delivery by NOT having a 
     dictionary keyed lookup mechanism.  Some loss of generality is implied 
     by this.
    
     This class/system is NOT thread safe, but could be made so with some
     mutex wrappers.  It is safe to call Attach/Detach as a consequence 
     of calling Notify(...).  
    
     */
    
    
    class Notified;
    
    class Notifier : public SingletonDynamic<Notifier>
    {
    public:
       typedef enum
       {
          NE_MIN = 0,
          NE_DEBUG_BUTTON_PRESSED = NE_MIN,
          NE_DEBUG_LINE_DRAW_ADD_LINE_PIXELS,
          NE_DEBUG_TOGGLE_VISIBILITY,
          NE_DEBUG_MESSAGE,
          NE_RESET_DRAW_CYCLE,
          NE_VIEWPORT_CHANGED,
          NE_MAX,
       } NOTIFIED_EVENT_TYPE_T;
    
    private:
       typedef vector<NOTIFIED_EVENT_TYPE_T> NOTIFIED_EVENT_TYPE_VECTOR_T;
    
       typedef map<Notified*,NOTIFIED_EVENT_TYPE_VECTOR_T> NOTIFIED_MAP_T;
       typedef map<Notified*,NOTIFIED_EVENT_TYPE_VECTOR_T>::iterator NOTIFIED_MAP_ITER_T;
    
       typedef vector<Notified*> NOTIFIED_VECTOR_T;
       typedef vector<NOTIFIED_VECTOR_T> NOTIFIED_VECTOR_VECTOR_T;
    
       NOTIFIED_MAP_T _notifiedMap;
       NOTIFIED_VECTOR_VECTOR_T _notifiedVector;
       NOTIFIED_MAP_ITER_T _mapIter;
    
       // This vector keeps a temporary list of observers that have completely
       // detached since the current "Notify(...)" operation began.  This is
       // to handle the problem where a Notified instance has called Detach(...)
       // because of a Notify(...) call.  The removed instance could be a dead
       // pointer, so don't try to talk to it.
       vector<Notified*> _detached;
       int32 _notifyDepth;
    
       void RemoveEvent(NOTIFIED_EVENT_TYPE_VECTOR_T& orgEventTypes, NOTIFIED_EVENT_TYPE_T eventType);
       void RemoveNotified(NOTIFIED_VECTOR_T& orgNotified, Notified* observer);
    
    public:
    
       virtual void Reset();
       virtual bool Init() { Reset(); return true; }
       virtual void Shutdown() { Reset(); }
    
       void Attach(Notified* observer, NOTIFIED_EVENT_TYPE_T eventType);
       // Detach for a specific event
       void Detach(Notified* observer, NOTIFIED_EVENT_TYPE_T eventType);
       // Detach for ALL events
       void Detach(Notified* observer);
    
       /* The design of this interface is very specific.  I could 
        * create a class to hold all the event data and then the
        * method would just have take that object.  But then I would
        * have to search for every place in the code that created an
        * object to be used and make sure it updated the passed in
        * object when a member is added to it.  This way, a break
        * occurs at compile time that must be addressed.
        */
       void Notify(NOTIFIED_EVENT_TYPE_T, const void* eventData = NULL);
    
       /* Used for CPPUnit.  Could create a Mock...maybe...but this seems
        * like it will get the job done with minimal fuss.  For now.
        */
       // Return all events that this object is registered for.
       vector<NOTIFIED_EVENT_TYPE_T> GetEvents(Notified* observer);
       // Return all objects registered for this event.
       vector<Notified*> GetNotified(NOTIFIED_EVENT_TYPE_T event);
    };
    
    /* This is the base class for anything that can receive notifications.
     */
    class Notified
    {
    public:
       virtual void Notify(Notifier::NOTIFIED_EVENT_TYPE_T eventType, const void* eventData) = 0;
       virtual ~Notified();
    
    };
    
    typedef Notifier::NOTIFIED_EVENT_TYPE_T NOTIFIED_EVENT_TYPE_T;
    
    通知程序本身添加了相应的Notify(…)方法。所有这些方法都使用单个函数来获取“目标列表”,然后在目标上调用相应的函数。这可以很好地工作,并使接收器不必执行难看的强制转换


    <>这似乎很好。这个解决方案和源代码一起发布在网络上。这是一个相对新的设计,所以任何反馈都是非常值得赞赏的。

    < P>我写了很多C++代码,需要为我正在开发的一些游戏组件创建一个观察者。我需要一些东西来分发“框架的开始”、“用户输入”。等,作为游戏中的事件发送给感兴趣的各方

    <>我也希望它是直的C++,不依赖于平台或特定的技术(比如Boost、Qt等),因为我经常在不同的项目中构建和重用组件(以及它们背后的想法)。 以下是我提出的解决方案的大致草图:

  • 观察器是一个单例,它有键(枚举值,而不是字符串)供主题注册感兴趣的对象。因为它是单例,所以它总是存在的
  • 每个主题都派生自一个公共基类。基类有一个抽象虚函数Notify(…),必须在派生类中实现,还有一个析构函数