Паттерн был опубликован в классической работе Влиссидеса в далеком 1997 году как вариация известного всем Observer.
В отличие от Observer, паттерн Typed Message полагается не на ручную диспечеризацию сообщений с помощью dynamic_cast, а на автоматическую проверку типов компилятором. Именно это свойство Typed Message делает этот паттерн лучшей релизацией идеи широковещательных сообщений на подобии Observer.
При попытке использовать реализацию предложеную Полом Пелетье в той же работе Влиссидеса пользователь сталкивается с неприятностями ввиде выделения лишней памяти, краха процесса и просто неудобства работы с шаблоном.
Изначальный код (переименованы имена членов и параметров шаблона класса и его имя):

template
<
class MessageType
>
class TypedMessage
{
public:
class Listener
{
public:
Listener()
{
TypedMessage::attach( this );
}virtual int acceptMessage( const MessageType & ) = 0;
};

typedef
std::list
ListenerList;

static void attach( Listener* aListener )
{
registry.push_back(aListener);
}

static void notify( TypedMessage *message )
{
for ( ListenerList::iterator i = registry.begin();
i != registry.end(); i++)
{
(*i)->acceptMessage( * (MessageType *) message );
}
}

void notify()
{
MessageType::notify( this );
}

private:
static ListenerList registry;
};
Реализация нарушает важнейший принцип C++ "получение ресурса есть инициализация" довольствуясь дестуктором по-умолчанию и принцип инкапсуляци. Для устранения этих ошибок достаточно определить деструктор в класс Listener, который удалял бы из списка оповещения слушателя сообщения, а за одно переопределить области видимости:

template
<
class MessageType
>
class TypedMessage
{
public:
struct Listener
{
Listener()
{
TypedMessage::attach( this );
}~Listener()
{
TypedMessage::detach( this );
}

virtual int acceptMessage( const MessageType & ) = 0;
};

void notify()
{
MessageType::notify( this );
}

private:
friend
class Listener;

typedef
std::list
ListenerList;

static void attach( Listener *listener )
{
ms_registry.push_back( listener );
}

static void detach( Listener *listener )
{
ms_registry.erase( std::find( ms_registry.begin(),
ms_registry.end(),
listener ) );
}

static void notify( TypedMessage *message )
{
for ( ListenerList::iterator i = ms_registry.begin();
i != ms_registry.end(); i++)
{
(*i)->acceptMessage( * (MessageType *) message );
}
}

private:

static ListenerList ms_registry;
};

Еще один недостаток связан с использованинем данного шаблона. Дело в том, что для объявления сообщения недостаточно написать что-то вроде:

struct MyMessage: TypedMessage<MyMessage> {};

Необходимо определять член ms_registry каждый раз для нового сообщения, что крайне нежелательно так как требует от пользователя предельной внимательности, что недопустимо. Для устранения этого недостатка можно применить подход аналогичный реализации синлгтона Маерса, т.е. введем скрытую статическую функцию-член для доступа к единственному экземпляру реестра получателей:

static ListenerList &getRegistry()
{
static std::auto_ptr holder(new ListenerList);
return * holder.get();
}

Как показала практика использование статического управленца динамической памяти a la std::auto_ptr в большинстве случаев предотвращает крах программы пользователя на этапе разрушения статических объектов. Хотя желательно все таки для этих целей использовать феникс-синглтон из библиотеки Loki.
Получена базовая стабильная реализация шаблона Typed Message. Если применить к ней подход расщепления шаблона на стратегии, получится очень мощная выразительная идиома.

google.com bobrdobr.ru del.icio.us technorati.com linkstore.ru news2.ru rumarkz.ru memori.ru moemesto.ru
          0 голосов

2 Комментариев на “Доработка паттерна Typed Message”

  1. seagull сказал:

    Класс хорош - не спорю :)

    Интересует уточнение как и где удобен в применении.
    Ведь если, Дима, пишешь о таком паттерне, значит применял.

    Поведай нам где такое удобно использовать.

  2. doc сказал:

    Для описание патернов ГОФ разработали целую систему, в которую входят определенные пукты. Не хватает картинок.

    ЗЫ: Хороший вклад. Лиха беда начало.





Оставте свое мнение