2013-06-18 1 views
1

У меня разные посетители для определенных типов объектов. У меня проблема с внедрением общего интерфейса, который может использоваться для всех типов. Какая лучшая архитектура используется в этой ситуации? Я придумал 3 различных решений, но все они выглядят очень некрасиво со мной: (некоторые вещи, как виртуальные деструкторы отбрасываются для простоты)Шаблон посетителя против downcasting с ограничением на тип ввода

class IObject { 
    virtual void Accept(IVisitor& visior) = 0; 
}; 

class Text: IObject { 
    void Accept(IVisitor& visitor) { 
     visitor.Visit(*this); 
    } 
}; 

class Image: IObject { 
    void Accept(IVisitor& visitor) { 
     visitor.Visit(*this); 
    } 
}; 

class IVisitor { 
    virtual void Visit(Text& text) = 0; 
    virtual void Visit(Image& image) = 0; 
}; 

class TextVisitor: IVisitor { 
    void Visit(Text& text) { 
     // Do some stuff with text 
    } 

    void Visit(Image& image) { 
     // Image not supported, throw exception 
    } 
}; 

ИЛИ

class IObject {}; 
class Text: IObject {}; 
class Image: IObject {}; 

class IVisitor { 
    virtual void Visit(IObject& object) = 0; 
}; 

class TextVisitor: IVisitor { 
    void Visit(IObject& object) { 
     Text& text = dynamic_cast<Text&>(object); 
     // Do some stuff with text 
    } 
}; 

ИЛИ

template <typename T> 
class IVisitor { 
    virtual void Visit(T& object) = 0; 
}; 

class TextVisitor: IVisitor<Text> { 
    void Visit(Text& text) { 
     // Do some stuff 
    } 
}; 

class ImageVisitor: IVisitor<Image> { 
    void Visit(Image& image) { 
     // Do some stuff 
    } 
}; 

class ITextImagelVisitor: IVisitor<Text>, IVisitor<Image> {}; 

class VisitorDispatcher: ITextImageVisitor { 
    void Visit(Text& text) { 
     text_visitor_->Visit(text); 
    } 

    void Visit(Image& image) { 
     image_visitor_->Visit(image); 
    } 

    std::shared_ptr<IVisitor<Text>> text_visitor_; 
    std::shared_ptr<IVisitor<Image>> image_visitor_; 
}; 

class IObject { 
    virtual void Accept(ITextImageVisitor& visior) = 0; 
}; 

class Text: IObject { 
    void Accept(ITextImageVisitor& visitor) { 
     visitor.Visit(*this); 
    } 
}; 

class Image: IObject { 
    void Accept(ITextImageVisitor& visitor) { 
     visitor.Visit(*this); 
    } 
}; 
+0

В классическом (GoF) шаблоне посетителей используется подход №1. Это также, безусловно, предпочтительнее №2. Что вам не нравится в этом? – Angew

ответ

2

Ответ, очевидно, зависит от того, что вы пытаетесь сделать достичь здесь. Шаблон посетителя используется для работы с составленными объектами, например. деревьев и вызывает себя на подобъектах этой композиции. В вашем примере это будет текст, содержащий тексты и изображения, содержащие тексты и изображения ... Это явно не имеет большого смысла, поэтому, если вы : на самом деле работает с текстами и изображениями, посетитель может не быть что вам нужно, и вы должны предоставить дополнительную информацию о том, чего вы пытаетесь достичь.

Для ваших различных кодов:

  1. выглядит хорошо, это является допустимым выполнение посетителя. Посетитель будет работать над любым композитом, который не содержит изображений.
  2. выглядит плохо, из-за dynamic_cast. Все, что касается шаблона посетителя, заключается в том, чтобы избежать таких отбросов, и этот не расширяется. Рассмотрим, например, еще несколько типов в иерархии объектов, например. звуковые файлы, видео и т. д. использование dynamic_cast здесь вам не поможет. И если вы хотите поддерживать только один тип объекта, вам не нужен посетитель.
  3. выглядит еще хуже. Ваш VisitorDispatcherнаследует от Visitor<Text> и содержит a Visitor<Text>. В лучшем случае это странный дизайн.
0

Обратите внимание, что вашему классу Visitor нужна информация о производных классах Object. Вы хотите сделать полиморфизм, но вы застреваете, создавая отдельного посетителя для каждого нового производного Object. Чтобы сделать эту работу, вам нужно будет сделать ваш Object более универсальным. Я предполагаю, что вы пытаетесь создать какой-то механизм для рисования Object s на экране, поэтому для каждого объекта также нужны virtual int width() = 0 и virtual int height() = 0, которые реализуются с помощью дериваторов. Тогда понадобится также абстрактный метод virtual void draw() = 0 для Object. Таким образом, вам не нужен class TextVisitor, а также не class ImageVisitor, Visitor может просто позвонить object->draw() на каждые Object, которыми он посещает.

+0

Дело в том, что я не хочу помещать эту реализацию в объект, потому что может быть много методов рисования для одного и того же типа данных (кстати, это не рисунок на экране, но это не имеет значения). Например. в случае текста объект является просто строкой, и он не должен знать о различных методах обработки, которые могут быть применены к этой строке. – lizarisk

+0

Действительно, «Объект» не должен знать о различных методах обработки, которые могут быть применены к «Тексту», но он должен _expose_ возможность обработки через чистый виртуальный метод, который реализуется с помощью «Текста». – rwols

+0

Я думаю, что сам 'Текст' тоже не должен знать о его методах обработки. – lizarisk