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

はそのような表現の 1 つです。

さらに、これらの操作はすべてまとめて実行する必要があります。つまり、base_data* オブジェクトのコレクションを通じてポリモーフィックに実行する必要があります。

この問題の解決策の 1 つは、次のように 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;

しかし、繰り返しますが、これはポリモーフィックではありません。

私が読んだほとんどの「変換」設計パターンはインターフェイスを扱っていますが、ポリモーフィズムのコンテキストでデータの変換/変換を正式に扱うものは見つかりませんでした。

この問題に正式に対処する設計パターンへの参照、実装の進め方に関するアドバイス、および/または私のアプローチの明らかな欠陥の指摘を探しています。

4

1 に答える 1

1

コメントで要求されているように、私が説明したビジター パターンの使用を示すコードが以下にあります。入力、JSON、CSV、または必要な形式の訪問者を追加するだけです。さまざまなレコード構造を処理するために訪問者を変更する必要がないことに注意してください。以下の実装では、仮想ディスパッチを介して関連するさまざまなフィールド タイプを処理する方法を知る必要があるだけです。これらはすべて、Boost Serialization Library に似ています。これも参照することをお勧めします。

#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>
于 2015-06-11T13:56:00.277 に答える