Фабричные методы в производных классах

У меня есть два типа сообщений: MessageA и MessageB, оба производные от абстрактного класса IMessage, содержащего чистый виртуальный метод std::string toString(). Поэтому я могу преобразовать каждое сообщение в его строковое представление с указателем на базовый класс. Это нормально. Однако мне нужно как-то построить сообщение (конкретный тип сообщения) из строки, например: MessageA* msg = [something].fromString( str ). Я хотел бы получить NULL, если данная строка не подходит для построения MessageA. Я вижу два подхода к этой задаче:

а) MessageFactory с dynamic_cast

    class MessageFactory
    {
        IMessage* fromString( const std::string& str );
    };

    ...
    MessageFactory mf;
    MessageA* msg = dynamic_cast< MessageA* >( mf.fromString( str ) );
    if ( msg ) { ... } 

Однако при этом используется dynamic_cast, которого я хотел бы избежать.

б) фабричный метод в каждом производном классе

static MessageA* fromString( const std::string& str )
{
    return stringIsOk( str ) ? new MessageA() : NULL;
}

Есть ли лучшее решение? Должен ли я что-то изменить в общем дизайне? Спасибо.

ОБНОВЛЕНИЕ

Иногда я знаю, какое сообщение я должен получить из строки, т.е.

void sendRequest()
{
 ...
 std::string response;
 MessageA* msg = fromString( response );
 // here i should only check if string is valid for MessageA
}

но иногда я не знаю, что придет ко мне:

void processMessage( const std::string& str )
{
     IMessage* msg = fromString( str );
     if ( msg )
     {
         MessageA* msgA = dynamic_cast< MessageA* >( msg );
         if ( msgA )
         ...
     } 
}

person Kael    schedule 01.07.2013    source источник
comment
Вам действительно нужно, чтобы это было MessageA* ? IMessage* недостаточно?   -  person Asha    schedule 01.07.2013
comment
Это похоже на злоупотребление. Если вы уже знаете, что хотите MessageA и только MessageA, вы можете написать прямую функцию синтаксического анализа (в идеале возвращающую boost::optional<MessageA>). Полиморфная иерархия кажется ненужной. Типичная фабрика вернет IMessage *.   -  person Kerrek SB    schedule 01.07.2013
comment
@KerrekSB Мне нужно получать разные сообщения. Когда-то я могу ожидать его тип, когда-то нет. Механизм обработки сообщений должен обрабатывать каждый тип сообщения по-разному.   -  person Kael    schedule 01.07.2013
comment
Можете попробовать сделать там, где вы его используете, виртуальную функцию, объявленную в интерфейсе? Тогда вы можете просто использовать его через интерфейс   -  person doctorlove    schedule 01.07.2013
comment
@Kael Вы можете поместить различия в IMessage с помощью виртуальных методов, поэтому вам не нужен конкретный тип. Если вы не хотите помещать логику в классы сообщений, используйте шаблон посетителя/двойной отправки.   -  person Arne Mertz    schedule 01.07.2013
comment
@Kael: это вполне может быть, но конкретная ситуация, которую вы описываете, не требует полиморфизма во время выполнения. Это прямой вопрос, можно ли анализировать строку как Foo или нет.   -  person Kerrek SB    schedule 01.07.2013


Ответы (1)


Вы можете заставить все классы, производные от IMessage, регистрировать себя (или, скорее, конкретную фабрику) с классом MessageFactory статически при запуске программы. Это может быть достигнуто с помощью некоторого статического экземпляра. Затем интерфейс IMessage должен иметь чистый виртуальный метод canConstructFrom(string&), чтобы при поступлении строки вы передавали ее в MessageFactory, и фабрика определяла, какой из производных классов можно создать из этой строки, и создавала правильный экземпляр.

person arne    schedule 01.07.2013
comment
Спасибо, это хорошее решение. Я в той же ситуации, что описана здесь stackoverflow.com/questions/2758449/ - person Kael; 02.07.2013