web-dev-qa-db-ja.com

クラスのメソッドはそれ自体を変更した後、いつ同じインスタンスを返す必要がありますか?

A()B()C()の3つのメソッドを持つクラスがあります。これらのメソッドは、独自のインスタンスを変更します。

インスタンスが別個のコピーである場合(Clone()と同様)、メソッドはインスタンスを返す必要がありますが、voidまたは同じインスタンス(_return this;_を返す自由な選択肢を得ました)メソッド内の同じインスタンスを変更し、他の値を返さない場合。

同じ変更されたインスタンスを返すことを決定するとき、obj.A().B().C();のようなきちんとしたメソッドチェーンを実行できます。

これがそうする唯一の理由でしょうか?

自分のインスタンスを変更して返すこともできますか?それとも、コピーを返して元のオブジェクトを以前のままにしておくべきですか?同じ変更されたインスタンスを返す場合、ユーザーは戻り値がコピーであると想定する可能性があるため、それ以外の場合は返されませんか?それが大丈夫なら、そのようなことをメソッドで明確にする最良の方法は何ですか?

9
Martin Braun

連鎖を使用する場合

関数チェーンは、IDEオートコンプリートが一般的な場所である言語でよく使用されます。たとえば、ほとんどすべてのC#開発者はVisual Studioを使用します。したがって、C#で開発している場合、 Visual Studioはチェーンの構築を支援するため、メソッドはそのクラスのユーザーにとって時間の節約になります。

一方、PHPのような、本質的に非常に動的で、IDEでオートコンプリートをサポートしていないことが多い言語では、チェーンをサポートするクラスが少なくなります。チェーンは、正しい場合にのみ適切ですphpDocsは、チェーン可能なメソッドを公開するために使用されます。

連鎖とは何ですか?

Fooという名前のクラスがある場合、次の2つのメソッドはどちらもチェーン可能です。

_function what() { return this; }
function when() { return new Foo(this); }
_

1つが現在のインスタンスへの参照であり、新しいインスタンスを作成するという事実は、これらがチェーン可能なメソッドであることを変更しません。

連鎖可能なメソッドが現在のオブジェクトのみを参照する必要があるという絶対的なルールはありません。実際、連鎖可能なメソッドは2つの異なるクラスにまたがることがあります。例えば;

_class B { function When() { return true; } };
class A { function What() { return new B(); } };

var a = new A();
var x = a.What().When();
_

上記の例では、thisへの参照はありません。コードa.What().When()はチェーンの例です。興味深いのは、クラス型Bが変数に割り当てられないことです。

メソッドは、その戻り値が式の次のコンポーネントとして使用されるようになると連鎖します。

ここにいくつかの例があります

_ // return value never assigned.
 myFile.Open("something.txt").Write("stuff").Close();

// two chains used in expression
int x = a.X().Y() * b.X().Y();

// a chain that creates new strings
string name = str.Substring(1,10).Trim().ToUpperCase();
_

thisnew(this)を使用する場合

ほとんどの言語の文字列は不変です。したがって、メソッド呼び出しをチェーンすると、常に新しい文字列が作成されます。 StringBuilderのようなオブジェクトを変更できる場所。

一貫性がベストプラクティスです。

オブジェクトの状態を変更してthisを返すメソッドがある場合は、新しいインスタンスを返すメソッドを混在させないでください。代わりに、これを明示的に実行するClone()という特定のメソッドを作成します。

_ var x  = a.Foo().Boo().Clone().Foo();
_

a内で何が行われているのかは、はるかに明確です。

外のステップと裏技

これをステップアウトおよびバックトリックと呼びます。これは、チェーンに関連する多くの一般的な問題を解決するためです。これは基本的に、元のクラスから新しい一時クラスに降りてから、元のクラスに戻ることを意味します。

一時クラスは、元のクラスに特別な機能を提供するためだけに存在しますが、特別な条件下でのみ存在します。

チェーンを変更する必要がある場合がしばしばありますstateですが、クラスAは、これらすべての可能なstates。したがって、チェーン中に、Aへの参照を含む新しいクラスが導入されます。これにより、プログラマーは状態にステップインし、Aに戻ることができます。

これが私の例です。特別な状態をBと呼びます。

_class A {
    function Foo() { return this; }
    function Boo() { return this; }
    function Change() return new B(this); }
}

class B {
    var _a;
    function (A) { _a = A; }
    function What() { return this; }
    function When() { return this; }
    function End() { return _a; }
}

var a = new A();
a.Foo().Change().What().When().End().Boo();
_

これは非常に単純な例です。より細かく制御したい場合は、Bが、異なるメソッドを持つAの新しいスーパータイプに戻る可能性があります。

7
Reactgular

これらのメソッドは、独自のインスタンスを変更します。

言語によっては、void/unitを返し、それらのインスタンスまたはパラメーターを変更するメソッドを持つことは慣用的ではありません。以前はそれ以上のことを行っていた言語(C#、C++)でも、より機能的なスタイルのプログラミング(不変オブジェクト、純粋な関数)へのシフトにより時代遅れになっています。しかし、今のところ正当な理由があると仮定しましょう。

これがそうする唯一の理由でしょうか?

特定の動作の場合(x++)変数を変更しても、操作が結果を返すことが期待されます。しかし、それが主に自分で行う唯一の理由です。

自分のインスタンスを変更して返すこともできますか?それとも、コピーを返して元のオブジェクトを以前のままにしておくべきですか?同じ変更されたインスタンスを返す場合、ユーザーは戻り値がコピーであることを認める可能性があるため、それ以外の場合は返されませんか?それが大丈夫なら、そのようなことをメソッドで明確にする最良の方法は何ですか?

場合によります。

Copy/newとreturnが一般的な言語(C#LINQとstring)では、同じ参照を返すと混乱を招きます。変更と復帰が一般的な言語(一部のC++ライブラリ)では、コピーは混乱を招きます。

voidを返すか、プロパティのような言語構造を使用して)署名を明確にすることは、明確にする最良の方法です。その後、SetFooのように名前を明確にして、既存のインスタンスを変更していることを示します。 ただし、重要なのは、使用している言語/ライブラリのイディオムを維持することです。

3
Telastyn

(私はあなたのプログラミング言語としてC++を想定しました)

私にとって、これは主に可読性の側面です。 A、B、Cが修飾子の場合、特にこれがパラメーターとして関数に渡される一時オブジェクトである場合は特にそうです。

do_something(
      CPerson('name').age(24).height(160).weight(70)
      );

に比べ

{
   CPerson person('name');
   person.set_age(24);
   person.set_height(160);
   person.set_weight(70);
   do_something(person);
}

変更されたインスタンスへの参照を返すのに問題がなければ、再グレードします。そうします。たとえば、ストリーム演算子 '>>'および '<<'( http://www.cprogramming。 com/tutorial/operator_overloading.html

0
AdrianI

また、メソッドリターンを変更するのではなく、コピーリターンで処理をチェーンすることもできます。

良いC#の例はstring.Replace(a、b)です。これは、それが呼び出された文字列を変更しませんが、代わりに新しい文字列を返します。

0
Peter Wone