2011-11-16 10 views
7

これには名前がありますか:これは設計パターンですか?これはセッターから返されますか?

class A 
{ 
    A* setA() 
    { 
     //set a 
     return this; 
    } 
    A* setB() 
    { 
     //set b 
     return this; 
    } 
}; 

次のようなことができます:

A* a = new A; 
a->setA()->setB(); 

これには欠点はありますか?利点は?

+7

主な欠点は、あなたが '' A& '*としてthis'を返す、と' *のA =新しいAを書くことができることです。 a-> setA()。setB() 'または(もっと重要なことに)' A b; b.setA()。setB(); '。 –

答えて

9

それは連鎖方法(FAQ link)として知られています、そしてより一般的に言及はなく、ポインタを使用して行います。私は今、この答えの最初のバージョンを掲示した後、Steve Jessop discusses in his answerことを見るように

メソッドチェーンが強く、パラメータイディオムFAQ link)名前付きに関連しています。 NPIイディオムは、コンストラクタ呼び出しを複雑にすることなく、多数のデフォルト引数を提供する簡単な方法の1つです。たとえば、GUIプログラミングに関係します。

メソッド連鎖の潜在的な問題は、継承階層のクラスにNPIイディオムを適用する必要がある場合です。次に、C++が共変法をサポートしていないことがわかります。それは、あなたの目がクラス継承の連鎖の中でクラスを上下にさまようとき、共変のメソッドは、あなたのさまよう目にクラスと同じ方法で特異性が変化するいくつかの型を含むメソッドです。’

cloneメソッドを定義する場合とほぼ同じ問題です。このメソッドは、すべてのクラスで同じテキスト定義を持ちますが、型を正しく取得するために各クラスで面倒に繰り返す必要があります。

この問題は言語サポートなしでは解決しません。それは本質的に複雑な問題、C++型システムとの一種の矛盾であるように見えます。私の“How to do typed optional arguments in C++98” blog postは、共変定義の生成を自動化するための関連するソースコードへのリンクと、Dr。Dobbs Journalに書いた記事へのリンクです。たぶん、私はC++ 11のためにそれを再訪するかもしれません。複雑さと可能な脆さがそれより大きなコストとして現れるかもしれないからです。’ s worth…

乾杯& HTH、

+0

ここのFAQリンクはISO CPPに更新する必要があります。私は間違って誰かの編集を拒否しました。 (http://meta.stackexchange.com/questions/249938/i-incorrectly-rejected-some-edits-what-to-do) –

4

以前は「method chaining」のようなものがありましたが、私はそれをデザインパターンと呼んでいません。 (一部の人々はまた、この使用して「fluent interface」を実施について話す - 私はそれが前に、しかし、しばらく前にそれについて書かれているようにMartin Fowler思われることと呼ばれる見たことがないと思います)あなたはこれを行うことによって、多くを失うことはありません

- あなたはいつものようにそれを使用したくない場合は、返信結果を非常に喜んで無視することができます。

これは価値があると私は不安です。いくつかの状況では非常に謎めいている可能性があります。しかし、基本的にが必要です。ストリームベースのIOの場合はoperator<<のようなものにはが必要です。私はそれがコードの残りの部分とどのように適合するかを確認する必要があると言いたい - それを読んでいる人には明らかであろうか?

0

それは一般的に例えば、ブースト用で使用されている(スティーブ・ジェソップは指摘したように、これは、ほとんどの場合、しかし、ではないポインタを参照して行われている)が、ほとんどの時間は、機能ではなく、参照を返します。

A &setX() 
{ 
    // ... 
    return *this; 
} 
あなたはAからクラスを派生場合
2

一つの欠点は、このようにと言っている:

class Foo : public A 
{ 
public: 
    Foo *setC() 
    { 
    // set C 
    return this; 
    } 
}; 

、あなたがセッターを呼び出す順序が重要です。最初のFooのすべてのセッターを呼び出す必要があります:たとえば、これは動作しません。

Foo f=new Foo(); 
f->setA()->setC(); 

この意志のに対し:

Foo f=new Foo(); 
f->setC()->setA(); 
+0

良い点 - これは '(std :: ostringstream()<<" hi "<< 0のようなものを書くことができない理由です。 ).str() 'これはうまくいけば非常に便利なイディオムになります。 – Flexo

+0

@awoodland:テンプレート std :: ostringstream&operator <<(std :: ostringstream&o、const T&t){static_cast (o)<< t;戻り値o; } '、またはそのテンプレートは' ostream& 'の既存の関数とあまり良くマッチしませんか? –

+0

@SteveJessop - 派生型は基本型よりもマッチしますが、今試したときにはうまくいかないようです。私はなぜそれを試してみるでしょう。 – Flexo

3

もう一つの共通っぽい使用がパラメータオブジェクト」であります"メソッド連鎖がなければ、設定が非常に不便ですが、一時的にすることもできます。

の代わりに:

complicated_function(P1 param1 = default1, P2 param2 = default2, P3 param3 = default3); 

書き込み:

struct ComplicatedParams { 
    P1 mparam1; 
    P2 mparam2; 
    P3 mparam3; 
    ComplicatedParams() : mparam1(default1), mparam2(default2), mparam3(default3) {} 
    ComplicatedParams &param1(P1 p) { mparam1 = p; return *this; } 
    ComplicatedParams &param2(P2 p) { mparam2 = p; return *this; } 
    ComplicatedParams &param3(P3 p) { mparam3 = p; return *this; } 
}; 

complicated_function(const ComplicatedParams &params); 

は今、私はそれを呼び出すことができます。パラメータの順番を覚えておく必要はありません、発信者を意味

complicated_function(ComplicatedParams().param2(foo).param1(bar)); 

を。でなければならないメソッドチェーンなし:

ComplicatedParams params; 
params.param1(foo); 
params.param2(bar); 
complicated_function(params); 

私もそれを呼び出すことができます。

complicated_function(ComplicatedParams().param3(baz)); 

オーバーロードのトンを定義することなく、私はちょうど最後のパラメータを指定することができることを意味していると残りはデフォルトのままにしておきます。

最終明白な微調整がcomplicated_functionComplicatedParamsのメンバーを作ることです。

struct ComplicatedAction { 
    P1 mparam1; 
    P2 mparam2; 
    P3 mparam3; 
    ComplicatedAction() : mparam1(default1), mparam2(default2), mparam3(default3) {} 
    ComplicatedAction &param1(P1 p) { mparam1 = p; return *this; } 
    ComplicatedAction &param2(P2 p) { mparam2 = p; return *this; } 
    ComplicatedAction &param3(P3 p) { mparam3 = p; return *this; } 
    run(void); 
}; 

ComplicatedAction().param3(baz).run(); 
関連する問題