При использовании Typed Message сообщения рассылаются всем адресатам, подписанных на получение сообщения.
Проблема возникает, когда сообщение не нужно посылать всем адресатам, если оно не может быть принято одним из них. Например, в каркасе приложения главное окно должно закрыться, и посылает всем дочерним окнам сообщение о закрытии, но какое-то дочернее окно не может закрыться прямо сейчас, соответственно адекватным поведением было бы прекращение дальнейшей рассылки сообщения о закрытии и отмене закрытия главного окна.
Существуют ситуации, когда рассылка одного и того же сообщения должна происходить избирательно по адресатам, например, учитывая их состояние.
Для решения поставленной задачи, был использован подход расширения класса стратегиями. В прежнем коде была заменена только скрытая статическая фукнция-член void TypedMessage::notify(StrictMessageType *message), поэтому далее приводится только измененный код.
namespace StdPolicies
{
struct AcceptAll
{
template< class ListenerType >
static bool overseeListener( ListenerType * ) { return true; }
template< class MessageType >
static bool overseeMessage( MessageType * ) { return true; }
};
} // namespace StdPolicies
template
<
class MessageType,
class NotificationFilterPolicy = StdPolicies::AcceptAll
>
class TypedMessage
{
typedef TypedMessage< MessageType, NotificationFilterPolicy> StrictMessageType;
//...............
static void notify( StrictMessageType *message )
{
for ( ListenerList::iterator listener = getRegistry().begin();
listener != getRegistry().end(); ++ listener)
{
MessageType &refMessage( * static_cast< MessageType * >(message) );
if ( NotificationFilterPolicy::overseeListener( *listener )
&& NotificationFilterPolicy::overseeMessage( & refMessage ) )
{
(*listener)->acceptMessage( refMessage );
}
}
}
//...............
};
Использовать расширенный Typed Message попрежнему легко. Для начала следует определить свой класс стратегии NotificationFilterPolicy, и указать его вместо эталонного.
struct CloseAllQueryFilter
{
template < class ListenerType >
static bool overseeListener( ListenerType * ) { return true; }
template < class MessageType >
static bool overseeMessage( MessageType *message )
{
return ! message->isRejected();
}
};
struct CloseAllQuery:
public Patterns::TypedMessage
<
CloseAllQuery,
CloseAllQueryFilter
>
{
CloseAllQuery( bool &canClose ): m_canClose( canClose ) {}
void reject() { m_canClose = false; }
bool isRejected() const { return ! m_canClose; }
private: mutable bool &m_canClose;
};
Теперь для того что бы рассылка прекратилась, адресат в обработчике сообщения в методе acceptMessage( const CloseAllQuery &msg ) должен вызвать msg.reject().
Теперь можно посмотреть на весь код.



ноября 9, 2006 в 12:21 pm
Немножко запутанный, но вполне читабельный шаблон и похоже, что достаточно юзабельный.
Было бы интересно создать подобное чудо для многопоточного приложения.