2

私はそれをよりユーザーフレンドリーにするために私が書いたのではないライブラリをラップしています。非常に基本的な関数が多数あるため、実際に必要なのが結果の型変換だけである場合に、これらすべてをラップする必要があるのは理想的ではありません。

不自然な例:

ライブラリにQueryServiceクラスがあり、特に次のメソッドがあるとします。

WeirdInt getId() const;

インターフェイスに標準のintが必要ですが、これを行う方法を知っているので、WeirdIntからintを問題なく取得できます。この場合、WeirdIntには次のものがあるとしましょう。

int getValue() const;

これは非常に単純な例であり、多くの場合、型変換はより複雑であり、必ずしもgetValue()の呼び出しだけではありません。

文字通り何百もの関数呼び出しがあり、これらのような型が常に追加されているので、ライブラリがWeirdTypeを変換するたびに、何百万ものメソッドを絶えず追加しなければならないという負担を軽減したいと思います。タイプ。

QueryServiceとすべて同じ機能を備えたQueryServiceWrapperを作成したいのですが、ここで型を変換しました。QueryServiceのすべてのメソッドをラップするために、同じ名前のメソッドを作成する必要がありますか?それとも私が見逃している魔法はありますか?それにももう少しありますが、この質問には関係ありません。

ありがとう

4

4 に答える 4

4

私が思う最初のアプローチは、次のようなテンプレートを試すことです。

  • getValue()簡単なメソッドを持つすべてのラッパータイプの標準実装を提供します
  • 他のすべてのテンプレートを専門にします

何かのようなもの:

class WeirdInt
{
  int v;
  public:
    WeirdInt(int v) : v(v) { }
    int getValue() { return v; }
};

class ComplexInt
{
  int v;
  public:
    ComplexInt(int v) : v(v) { }
    int getValue() { return v; }
};


template<typename A, typename B>
A wrap(B type)
{
  return type.getValue();
}

template<>
int wrap(ComplexInt type)
{
  int v = type.getValue();
  return v*2;
};

int x = wrap<int, WeirdInt>(WeirdInt(5));
int y = wrap<int, ComplexInt>(ComplexInt(10));
于 2013-01-27T00:45:34.780 に答える
1

のラッパーメソッドが単純なパターンを持っている場合は、ヒューリスティックを使用して、perlまたはpythonスクリプトQueryServiceで生成することも考えられます。QueryServiceWrapper次に、多くてもいくつかの入力パラメータを定義する必要があります。

いくつかのマクロを定義することでさえ、このラッパークラスを書くのに役立ちます。

于 2013-01-27T01:13:43.057 に答える
0

簡単に言うと、機能を完全にカプセル化して、「クライアント」コードに公開されないようにWeirdIntQueryService、クライアントコードでそれらを宣言するヘッダーを含める必要がないようにすることを目的としている場合は、採用するアプローチに疑問があります。どんな魔法からも恩恵を受けることができます。

以前にこれを行ったとき、私の最初のステップは、pimplイディオムを使用して、ヘッダーに次のような実装の詳細が含まれないようにすることでした。

QueryServiceWrapper.h

class QueryServiceWrapperImpl;

class QueryServiceWrapper
{
public:
    QueryServiceWrapper();
    virtual ~QueryServiceWrapper();

    int getId();
private:
    QueryServiceWrapperImpl impl_;
};

次に、定義に、実装の詳細を、ダウンストリームコードに浸出しないことを知って安全に配置できます。

QueryServiceWrapper.cpp

struct QueryServiceWrapperImpl
{
public:
    QueryService svc_;
};

// ...

int QueryServiceWrapper::getValue()
{
    return impl_->svc_.getId().getValue();
}

変換を行うためにどのような異なる方法を使用する必要があるかを知らなければ、ここに追加しすぎることは困難ですが、テンプレート関数を使用して最も一般的なタイプの変換を行うことはできます。

ここでの欠点は、すべてを自分で実装する必要があることです。本当に必要な機能だけを実装できるので、これは両刃の剣である可能性があります。通常、使用されないラッピング機能には意味がありません。

関数を実装する「銀の弾丸」、あるいは関数の空のラッパーさえも知りません。私は通常、シェルスクリプトを組み合わせて必要な空のクラスを作成するか、ヘッダーのコピーを取得し、sedまたはPerlを使用したテキスト操作を使用して、元の型をラッパークラスの新しい型に変更します。

このような場合、パブリック継承を使用して、関数をオーバーライドできるようにしながら、基本関数へのアクセスを有効にするのは魅力的です。Weirdただし、これは、リターンタイプを変更したい(オーバーロードには不十分)、および(おそらく)元のタイプの公開を防ぎたいため、この場合には適用されません。

ここで前進する方法は、集約を使用することですが、そのような場合、クラスの作成を自動化する準備ができていない限り、インターフェイス(の一部)の再実装を簡単に回避する方法はありません(コード生成を使用)範囲。

于 2013-01-27T01:19:10.313 に答える
-1

より複雑なアプローチは、元のファサードクラスに必要な数のファサードクラスを導入することですQueryService。各ファサードクラスには、1つの特定のクエリまたはクエリタイプに対して制限された関数のセットがあります。私はあなたの特定のことを知らないQueryServiceので、ここに架空の例があります:

  • 元のクラスに、奇妙なタイプで動作する多くの奇妙なメソッドがあるとします。

    struct OriginQueryService
    {
        WeirdType1 query_for_smth(...);
        WeirdType1 smth_related(...);
    
        WeirdType2 another_query(...);
        void smth_related_to_another_query(...);
    
        // and so on (a lot of other function-members)
    };
    
  • 次に、次のようなファサードクラスを作成できます。

    struct QueryFacade
    {
        OriginQueryService& m_instance;
    
        QueryFacade(OriginQueryService* qs) : m_instance(*qs) {}
    
        // Wrap original query_for_smth(), possible w/ changed type of
        // parameters (if you'd like to convert 'em from C++ native types to
        // some WeirdTypeX)...
        DesiredType1 query_for_smth(...);
        // more wrappers related to this particular query/task
        DesiredType1 smth_related(...);
    };
    
    struct AnotherQueryFacade
    {
        OriginQueryService& m_instance;
    
        AnotherQueryFacade(OriginQueryService* qs) : m_instance(*qs) {}
    
        DesiredType2 another_query(...);
        void smth_related_to_another_query(...);
    };
    

    すべてのメソッドデリゲートはm_instance、必要な方法で入力/出力タイプの変換を使用して呼び出しを委任し、装飾します。タイプ変換は、@Jackが彼の投稿で説明しているように実装できます。または、 ADLDesired fromWeird(const Weired&);によって選択される名前空間(およびなどWeired toWeired(const Desired&);)に一連の無料関数を提供することもできます。したがって、新しいタイプが発生した場合は、この2つの関数にオーバーロードを提供するだけです...このようなアプローチは機能しますでかなりうまくいきます。boost::serialization

    また、その関数のジェネリック(テンプレート)バージョンを提供することもできます。これは、たとえば、多くのタイプにそのようなメンバーがgetValue()ある場合に呼び出すものです。Weired

于 2013-01-27T01:09:01.003 に答える