Observer pattern (aka. Notifications) for wxWidgets
Posted: Mon Nov 01, 2004 4:34 pm
Hi. I've recently developed (not finished yet, I'm just polishing everything before releasing it) a group of classes for implementing the observer pattern and event forwarding under wxWidgets.
The idea is to implement notifications using the event system that wxWidgets provides. Furthermore, it allows arbitrary plugging of external event handler functions without disrupting the object's normal event handling mechanism.
The main features are:
* Event handling mechanism for wxEvtHandler-derived objects is unchanged (no hidden event handlers are "pushed" nor "popped").
* For non-wxEvtHandler-derived classes: you can make any class a "model" ("subject"), without requiring you inheriting from any specific class.
* You can extend existing wxEvtHandler-derived classes with forwarding capabilities wihout the limitations of wxWidgets (ie. dynamic method attachment to handle events can only be made to the handler object (you can't connect other object's handlers to a wxEvtHandler object); push/pop event handlers require right sequence for disconnecting a specific handler (that is only available in wxWindow-derived classes anyway), etc.).
* You can (if templates support is enabled) forward event handling to any class, without requiring the target class to be inherited from wxObject, wxEvtHandler or any other.
* If templates are not enabled, all you need is your forward destination class to be inherited from wxObject (not necessarilly from wxEvtHandler), so it's still very lightweight.
* You can disconnect, without knowing the particulars, all methods for a particular object in a single function call (see example below).
* You can attach a "strategy" for event processing regardless of each event handler's code (to simulate notifications, in which handlers should not interfere with each other).
* No changes to wxWidgets library are required, and it's (should be) fully compatible with all platforms (it does not need templates) wxWidgets supports.
* The same limitations apply to virtual functions as forward (or notification) destinations (they work as regular functions) as in wxWidgets.
As I said, it's almost finished. I found this crucial to implement the observer pattern.
In plain english, you could do this:
[syntax="c"]
// notifier as member (for model classes that should not be re-inherited from other classes).
class CMyClass1
{
public:
wxNotifier m_notifier;
void SomeMethod( void );
};
void CMyClass1::SomeMethod( void )
{
// operations...
// notify
m_notifier.ProcessEvent( wxEvent( m_id, wxEVT_MODEL_NOTIFICATIONS ) );
};
// notification as a transparent added mechanism for this wxEvtHandler-based model class
class CMyClass2 :
// its own inheritance (somehow derived from wxEvtHandler)
public wxEvtForwarderImpl
{
// ...
DECLARE_EVTFORWARDER_CLASS(CMyClass2)
};
IMPLEMENT_EVTFORWARDER_CLASS(CMyClass2)
void CMyClass2::SomeMethod( void )
{
// operations...
// notify
ProcessEvent( wxEvent( m_id, wxEVT_MODEL_NOTIFICATIONS ) );
};
class CMyManager // somehow derived from wxObject
{
protected:
// my specific model class in this context.
// could be a singleton referred in the code below, etc.
CMyClass1 * m_pModel1;
CMyClass2 * m_pModel2;
public:
void ProcessNotification( wxEvent& event );
void ProcessToolbarClicked( wxEvent& event );
};
// example
CMyManager::SetupConnections( void )
{
// ...
// using "notifier" object
pModel1->m_notifier.ConnectObj( ID_MODEL1, wxEVT_MODEL_NOTIFICATIONS, this, (wxObjectEventFunction) (wxEventFunction) &CMyManager::ProcessNotification, pMyCallbackData1 );
// using wxEvtHandler-derived class directly.
pModel2->ConnectObj( ID_MODEL2, wxEVT_MODEL_NOTIFICATIONS, this, (wxObjectEventFunction) (wxEventFunction) &CMyManager::ProcessNotification, pMyCallbackData2 );
// interesting: connect dynamically to handle a toolbar click
wxGetApp().ConnectObj( ID_CMDSOMEACTION, wxEVT_COMMAND_TOOL_CLICKED, this, (wxObjectEventFunction) (wxEventFunction) &CMyManager::ProcessToolbarClicked );
}
void CMyManager::ProcessNotification( wxEvent& event )
{
// handling code
}
void CMyManager::ProcessToolbarClicked( wxEvent& event )
{
// handling code
}
CMyManager::~CMyManager()
{
// safe 'disconnect all' method
m_pModel1->m_notifier.DisconnectObj( this );
m_pModel2->DisconnectObj( this );
wxGetApp().DisconnectObj( this );
}
[/syntax]
My post is mainly to ask people if there is an interest in having this sort of functionality in wxWidgets and to gather questions and suggestions. Basically, I want to know if it makes sense to continue this further, and make it go in a direction that people is interested in.
I've been using wxWidgets 2.4.2 as my reference code, but I don't think the functionality in 2.5.x is any different than 2.4.x's with respect to what I use.
Sorry for the clumsiness of the post but I don't have much time today.
Ideas, suggestions, comments are all welcome
The idea is to implement notifications using the event system that wxWidgets provides. Furthermore, it allows arbitrary plugging of external event handler functions without disrupting the object's normal event handling mechanism.
The main features are:
* Event handling mechanism for wxEvtHandler-derived objects is unchanged (no hidden event handlers are "pushed" nor "popped").
* For non-wxEvtHandler-derived classes: you can make any class a "model" ("subject"), without requiring you inheriting from any specific class.
* You can extend existing wxEvtHandler-derived classes with forwarding capabilities wihout the limitations of wxWidgets (ie. dynamic method attachment to handle events can only be made to the handler object (you can't connect other object's handlers to a wxEvtHandler object); push/pop event handlers require right sequence for disconnecting a specific handler (that is only available in wxWindow-derived classes anyway), etc.).
* You can (if templates support is enabled) forward event handling to any class, without requiring the target class to be inherited from wxObject, wxEvtHandler or any other.
* If templates are not enabled, all you need is your forward destination class to be inherited from wxObject (not necessarilly from wxEvtHandler), so it's still very lightweight.
* You can disconnect, without knowing the particulars, all methods for a particular object in a single function call (see example below).
* You can attach a "strategy" for event processing regardless of each event handler's code (to simulate notifications, in which handlers should not interfere with each other).
* No changes to wxWidgets library are required, and it's (should be) fully compatible with all platforms (it does not need templates) wxWidgets supports.
* The same limitations apply to virtual functions as forward (or notification) destinations (they work as regular functions) as in wxWidgets.
As I said, it's almost finished. I found this crucial to implement the observer pattern.
In plain english, you could do this:
[syntax="c"]
// notifier as member (for model classes that should not be re-inherited from other classes).
class CMyClass1
{
public:
wxNotifier m_notifier;
void SomeMethod( void );
};
void CMyClass1::SomeMethod( void )
{
// operations...
// notify
m_notifier.ProcessEvent( wxEvent( m_id, wxEVT_MODEL_NOTIFICATIONS ) );
};
// notification as a transparent added mechanism for this wxEvtHandler-based model class
class CMyClass2 :
// its own inheritance (somehow derived from wxEvtHandler)
public wxEvtForwarderImpl
{
// ...
DECLARE_EVTFORWARDER_CLASS(CMyClass2)
};
IMPLEMENT_EVTFORWARDER_CLASS(CMyClass2)
void CMyClass2::SomeMethod( void )
{
// operations...
// notify
ProcessEvent( wxEvent( m_id, wxEVT_MODEL_NOTIFICATIONS ) );
};
class CMyManager // somehow derived from wxObject
{
protected:
// my specific model class in this context.
// could be a singleton referred in the code below, etc.
CMyClass1 * m_pModel1;
CMyClass2 * m_pModel2;
public:
void ProcessNotification( wxEvent& event );
void ProcessToolbarClicked( wxEvent& event );
};
// example
CMyManager::SetupConnections( void )
{
// ...
// using "notifier" object
pModel1->m_notifier.ConnectObj( ID_MODEL1, wxEVT_MODEL_NOTIFICATIONS, this, (wxObjectEventFunction) (wxEventFunction) &CMyManager::ProcessNotification, pMyCallbackData1 );
// using wxEvtHandler-derived class directly.
pModel2->ConnectObj( ID_MODEL2, wxEVT_MODEL_NOTIFICATIONS, this, (wxObjectEventFunction) (wxEventFunction) &CMyManager::ProcessNotification, pMyCallbackData2 );
// interesting: connect dynamically to handle a toolbar click
wxGetApp().ConnectObj( ID_CMDSOMEACTION, wxEVT_COMMAND_TOOL_CLICKED, this, (wxObjectEventFunction) (wxEventFunction) &CMyManager::ProcessToolbarClicked );
}
void CMyManager::ProcessNotification( wxEvent& event )
{
// handling code
}
void CMyManager::ProcessToolbarClicked( wxEvent& event )
{
// handling code
}
CMyManager::~CMyManager()
{
// safe 'disconnect all' method
m_pModel1->m_notifier.DisconnectObj( this );
m_pModel2->DisconnectObj( this );
wxGetApp().DisconnectObj( this );
}
[/syntax]
My post is mainly to ask people if there is an interest in having this sort of functionality in wxWidgets and to gather questions and suggestions. Basically, I want to know if it makes sense to continue this further, and make it go in a direction that people is interested in.
I've been using wxWidgets 2.4.2 as my reference code, but I don't think the functionality in 2.5.x is any different than 2.4.x's with respect to what I use.
Sorry for the clumsiness of the post but I don't have much time today.
Ideas, suggestions, comments are all welcome