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

时间:2015-06-10 09:28:56

标签: c++ design-patterns polymorphism data-representation

问题如下:

考虑以下课程

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&#34;解决方案,可能是大规模执行这些翻译操作的能力。

如果我们考虑需要支持除any_map之外的许多表示的可能性,这也更有意义,例如。

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

但又一次,它不是多态的。

大多数&#34;翻译&#34;我读过关于处理接口的设计模式,但我还没有找到一个正式解决多态上下文中数据转换/转换的模式。

我正在寻找一个正式解决这个问题的设计模式的参考,如何继续实施和/或在我的方法中指出明显的缺陷的建议。

1 个答案:

答案 0 :(得分:1)

根据评论中的要求,下面的代码说明了我所描述的访客模式的使用。只需为输入,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>