多态数据转换/转换设计模式

Polymorphic Data Translation/Conversion Design Pattern

本文关键字:转换 设计模式 多态 数据      更新时间:2023-10-16

问题如下:

考虑以下类

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&);

因此,我们避免了派生类的污染,代价是"更少的 OO"解决方案,并且可能具有大规模执行这些转换操作的能力。

如果我们考虑需要支持除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>