Проблема заключается в следующем:
рассмотрим следующий класс
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;
Но еще раз, это не полиморфно.
Большинство шаблонов проектирования «перевода», о которых я читал, имеют дело с интерфейсами, но я не нашел такого, который бы формально рассматривал перевод / преобразование данных в контексте полиморфизма.
Я ищу ссылку на шаблон проектирования, который формально решает эту проблему, совет о том, как приступить к реализации и / или указать на явные недостатки в моем подходе.
Как указано в комментариях, ниже приведен код, иллюстрирующий использование шаблона посетителя, как я описал. Просто добавьте дополнительных посетителей для ввода, JSON, CSV или любых других форматов, которые вам требуются. Обратите внимание, что посетители не нуждаются в модификации для работы с различными структурами записей — нижеприведенная реализация просто должна знать, как обрабатывать различные типы полей, задействованные посредством виртуальной диспетчеризации. Все это в конечном итоге похоже на библиотеку Boost-сериализации, которую я тоже рекомендую рассмотреть.
#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>