2009-06-24 16 views
7

共通の機能と流暢なインターフェイスを備えた基底(抽象)クラス(type::baseと呼ぶ)を構築したいと思います。私が直面している問題は、流暢なインターフェイスとC++の継承

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

     base& with_foo(); 
     base& with_bar(); 
    protected: 
     // whatever... 
    }; 

今私は、サブタイプを作ることができる例えば:

my_type build_my_type() 
{ 
    return my_type().with_foo().with_bar(); 
} 
:このようなものをサブタイプを使用している場合

class my_type : public base { 
    public: 
     myType();   
     // more methods... 
    }; 

問題が来ます10

私たちはmy_typeではなくbaseを返すので、これはコンパイルされません。

私はちょうどことができることを知っている:

my_type build_my_type() 
{ 
    my_type ret; 
    ret.with_foo().with_bar(); 

    return ret; 
} 

しかし、私はどのように私はそれを実装することができます考えていた、と私は、いくつかの提案を任意の有効なアイデアを見つけていませんでしたか?

+0

質問とは何も関係がないので、名前空間のものを削除しました –

答えて

4

"タイプを失う"というこの問題はテンプレートで解決できますが、それはやや複雑です。

例:

class Pizza 
{ 
    string topping; 
public: 
    virtual double price() const; 
}; 

template <class T, class Base> 
class FluentPizza : public Base 
{ 
    T* withAnchovies() { ... some implementation ... }; 
}; 

class RectPizza : public FluentPizza<RectPizza, Pizza> 
{ 
    double price() const { return length*width; :) } 
}; 

class SquarePizza : public FluentPizza<SquarePizza, RectPizza> 
{ 
    ... something else ... 
}; 

あなたは、パターンではなく

class T : public B 

のあなたは

class T : public Fluent<T, B> 

別のアプローチは、上の流れるようなインターフェイスを使用していない可能性があり、書き込みということです

SquarePizza* p=(new SquarePizza)->withAnchovies(); 

を書くことができますオブジェクト、代わりに、ポインタに:このような

class Pizza { ... }; 
class RectPizza { ... }; 
class SquarePizza { ... whatever you might imagine ... }; 

template <class T> 
class FluentPizzaPtr 
{ 
    T* pizza; 
public: 
    FluentPizzaPtr withAnchovies() { 
    pizza->addAnchovies(); // a nonfluent method 
    return *this; 
    } 
}; 

用途:

FluentPizzaPtr<SquarePizza> squarePizzaFactory() { ... } 

FluentPizzaPtr<SquarePizza> myPizza=squarePizzaFactory().withAnchovies(); 
+0

例を教えてください。面白いかもしれない。 – liori

+0

cf.編集。面白いかもしれませんが、個人的に使うのかどうかはわかりませんが... – jpalecek

+0

実際にポインタに切り替えることができます。次に、単純な多型をCRTPなしで使用して、基底を返すことができます*。 – liori

-2

私はC#でそれを行うだろう、と私はそれがあまりにもC++に働くだろうと考えている方法はwith_foo()with_bar()のためのデフォルトの実装を提供することです...私のC#を許しますが、:で

class base { 
    virtual base with_foo() 
    { throw new NotImplementedException(); } 
    virtual base with_bar(); 
    { throw new NotImplementedException(); } 
} 
+0

これは問題を解決しません - 問題はタイプの不一致です。質問には関係ありません。 – jpalecek

-1

C++では、値ではなくポインターや参照を取り消す必要があります。また、「流暢なインターフェース」の意味を説明することもできます。

+2

http://en.wikipedia.org/wiki/Fluent_interface – liori

+1

申し訳ありません - 「Martin」という名前が表示されたら、私のbullsh * t検出器はフルボリュームで消えて、もう読むことはできません。姓や姓のどちらでもあれば問題ではないようです。 –

+0

Martin Fowlerの何が問題になっていますか? – Tobias

0

一つの解決策は、このように動作します:

return *static_cast<my_type*>(&my_type().with_foo().with_bar()); 

static_castを使用して、基本的に「私は私がここでやっているか知っている」コンパイラーに指示します。

5

あなたが参照/ポインタを返すべきである、とあなたは型情報を保持する必要はありません。

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

    base &with_foo(); 
    base &with_bar(); 
    protected: 
    // whatever... 
}; 

class my_type : public base { 
    public: 
    my_type();   
    // more methods... 
}; 

base *build_my_type() 
{ 
    return &new my_type()->with_foo().with_bar(); 
} 

すでに仮想デストラクタがあります。他の仮想機能があると思われます。基本型とそこに宣言されている仮想関数を通してすべてにアクセスしてください。

+0

問題は私がタイプを失いたくないということです。 – blaxter

関連する問題