2015-06-10 2 views
2

Проблемы заключается в следующем:Полиморфного Перевод/Дизайн Преобразования данных шаблон

рассмотрит следующий класс

class data : public base_data 
{ 
    public: 
    int a; 
    std::string b; 
    double c; 
    ... // many other members 
}; 

Предположит, что имеет смысл подвергать член данных этого класса.

Теперь рассмотрим, что существует много таких классов, каждый из которых имеет разные члены, возможно, все они происходят из одного базового класса «base_data».

Теперь эти классы необходимо экспортировать, импортировать, сконструировать, «установить» и «получить» из других произвольных представлений данных.

Например:

using any_map = boost::unordered_map < std::string, boost::any > ; 

одно такое представление.

Кроме того, все эти операции необходимо выполнять массово, то есть полиморфно через коллекцию объектов base_data *.

Одним из путей решения этой проблемы является создание интерфейса в base_data следующим

class base_data 
{ 
public: 
    virtual void set(const any_map&) = 0; 
    virtual any_map get() const = 0; 
}; 

каждый производный класс знает своих членов, поэтому он знает, как сделать перевод. дополнительно производные классы могут предоставить конструкторы формы

data(const any_map&) {...} 

Чтобы легко определить абстрактный заводской шаблон.

Другим решением этой проблемы является предоставление статических функций перевода под некоторым пространством имен для каждого производного типа, например.

static data convert(const any_map&); 
static any_map convert(const data&); 

Так мы избегаем загрязнения производных классов по стоимости решения «менее ОО» и, возможно, способность выполнять эти операции по переводу в массе.

Это также имеет больший смысл, если мы рассмотрим возможность поддержки многих представлений, отличных от any_map, например.

using boost::ptree; 
using json_class; 
using xml_class; 

Но еще раз, это не полиморфно.

Большинство шаблонов проектирования «перевода», которые я прочитал о сделке с интерфейсами, но я не нашел тот, который формально обращается к переводу/преобразованию данных в контексте полиморфизма.

Я ищу ссылку на шаблон дизайна, который формально решает эту проблему, советы о том, как продолжить реализацию и/или указывать на явные изъяны в моем подходе.

+0

Вы, вероятно, хотите, чтобы объяснить, почему сериализации (например, повышение :: Сериализация) не работает для вас. –

+0

* «Итак, мы избегаем загрязнения производных классов» * ... песок на пляже? * "также имеет гораздо больший смысл, если мы рассмотрим" * ... не так ли? Какое количество представлений связано с выбором полиморфного доступа к функциям перевода? Во всяком случае, я предлагаю вам использовать *** *** шаблон *** (через виртуальную диспетчеризацию для производных типов), с разными посетителями для каждого формата, с которым вы имеете дело: таким образом вы реализуете посетителя один раз за каждый класс, основанный на базе base_data , и его можно преобразовать в/из любого числа форматов. –

+0

н.м. не уверен, что я понимаю, как boost :: serialization решает что угодно. Я не эксперт в библиотеке, но насколько я понимаю, это обеспечивает худшее из обоих решений - изменение производных классов и отсутствие полиморфного поведения. Я мог ошибаться в последнем, но в любом случае это только один тип представления, который не обязательно является одним (или единственным), который мне понадобится. –

ответ

1

Как указано в комментариях, приведен код ниже, иллюстрирующий использование шаблона посетителя, как я описал. Просто добавьте дополнительных посетителей для ввода, JSON, CSV или любых других форматов, которые вам нужны. Обратите внимание, что посетители не нуждаются в изменении, чтобы иметь дело с различными структурами записей. Ниже приведенная реализация должна знать, как обрабатывать различные типы полей, связанные с виртуальной диспетчеризацией. Все это похоже на библиотеку с расширением сериализации, которую я также рекомендую посмотреть.

#include <iostream> 
#include <string> 
#include <vector> 
#include <sstream> 

struct Visitor 
{ 
    typedef const char* Identifier; // or string... 

    Visitor(std::ostream& os) : os_(os) { } 

    virtual Visitor& pre(Identifier) { return *this; } 
    template <typename T> Visitor& operator()(Identifier id, const T& t) 
    { 
     std::ostringstream oss; 
     oss << t; 
     return operator()(id, oss.str()); 
    } 
    virtual Visitor& operator()(Identifier, double) = 0; 
    virtual Visitor& operator()(Identifier, const std::string&) = 0; 
    virtual Visitor& post() { return *this; } 

    std::ostream& os_; 
}; 

struct Visitor__XML_Out : Visitor 
{ 
    using Visitor::Visitor; 

    Visitor& pre(Identifier i) override 
    { os_ << '<' << i << '>'; i_ = i; return *this; } 

    Visitor& operator()(Identifier f, double x) override 
    { return out(f, x); } 

    Visitor& operator()(Identifier f, const std::string& x) override 
    { return out(f, x); } 

    Visitor& post() override 
    { os_ << "</" << i_ << '>'; return *this; } 

    private: 
    template <typename T> 
    Visitor& out(Identifier f, const T& x) 
    { 
     os_ << '<' << f << '>' << x << "</" << f << '>'; 
     return *this; 
    } 

    Identifier i_; 
}; 

struct Base_Data 
{ 
    virtual void visit(Visitor& v) = 0; 
}; 

struct Data : Base_Data 
{ 
    int a_; 
    std::string b_; 
    double c_; 

    Data(int a, const std::string& b, double c) 
     : a_(a), b_(b), c_(c) 
    { } 

    void visit(Visitor& v) override 
    { 
     v.pre("Data")("a", a_)("b", b_)("c", c_).post(); 
    } 
}; 

int main() 
{ 
    Data d { 42, "hawk", 8.8 }; 
    Visitor__XML_Out xml(std::cout); 
    d.visit(xml); 
    std::cout << '\n'; 
} 

Выход:

<Data><a>42</a><b>hawk</b><c>8.8</c></Data> 
+0

Спасибо! Это решает проблему абстракции представления данных. Я понимаю, почему вы создали виртуальные методы для каждого базового типа, но это в основном кричит «шаблон» (и я понимаю, почему вы тоже не могли этого сделать). Может ли это быть улучшено? –

+0

@OmriBashari Вы можете настроить его по вкусу - чтобы проиллюстрировать, я удалил перегрузку 'Visitor' для' int 'и добавил шаблон, который предоставил обработку по умолчанию для произвольных типов. Причина, по которой вам все еще могут потребоваться некоторые жестко закодированные переопределения, заключается в том, что разные 'Visitor' могут использовать различные манипуляторы io (например,' std :: fixed', 'std :: setw',' std :: setprecision') или выполнять экранирование/цитирование типов (это действительно должно быть сделано для произвольных строк, помещенных в XML, но я не мог беспокоиться). Надеюсь, это ясно. Приветствия. –

Смежные вопросы