web-dev-qa-db-ja.com

単一のオブジェクトを渡す場合と複数のパラメーターを渡す場合

私が以下を持っていると仮定します

_Class A {
    Foo getFoo();
    Bar getBar();
    Baz getBaz();
}
_

そして、doStuffFooBar of oneオブジェクトを使用し、いくつかのことを行う関数Bazを定義する必要があります

doStuffを実装する方法のどちらが良いかの間で苦労しています(doStuffをクラスA内に配置するのは望ましくないと思われます)

方法A

_void doStuff(Foo foo, Bar bar, Baz baz)
{ 
    //some operation
}
_

または

方法B

_void doStuff(A a)
{
    Foo foo = a.getFoo();
    Bar bar = a.getBar();
    Baz baz = a.getBaz();
    //some operation
}
_

私の限られた知識に対して(+長所、短所)

方法A

+ doStuff()が操作するパラメーターが正確にわかる

-長いパラメーターリストの影響を受けやすく、ユーザーのミスの影響を受けやすい

方法B

+シンプルで使いやすい方法

+より拡張可能に見える(?)

-クラスAに対する不要な依存関係を作成します


誰もがこれらの2つの方法の長所と短所に関する追加の洞察を共有できますか?

26
Rufus

メソッドA(裸のパラメーター)には常に次の利点があります。

  • メソッドの作成者はパラメータオブジェクトを実装する必要がないため、タイプを少なくする必要があります。
  • パラメータのオブジェクトをインスタンス化する必要がないため、メソッドの呼び出し元はlessを入力する必要があります。
  • パラメータオブジェクトを構築してガベージコレクションを行う必要がないため、パフォーマンスが向上します。
  • 読者は、メソッドシグネチャのみから個々のパラメータを確認できます(ただし、これは両刃の剣です。以下を参照してください)。

メソッドB( パラメータオブジェクト )には、次のような利点があります。

  • パラメータはグループとしてドメインの意味を持つので、パラメータオブジェクトにその意味を説明する名前を付けることができ、読者がグループの各メンバーを読んで理解し、それらがどのように関連しているかを知る必要がなくなります。
  • パラメータリストは複数のメソッドで使用されるため、それぞれでパラメータオブジェクトを使用すると重複が減少します
  • パラメータリストの値は、複数のメソッド間でグループとして渡されます。これは、単一のパラメータオブジェクトとして渡せる場合に簡単です。
  • 一部の値の組み合わせは無効です。パラメータオブジェクトはこれらの組み合わせを防ぐことができます
  • 一部の値はオプションであり、デフォルトのパラメーター値またはオーバーロードされたメソッドの代わりに(言語に応じて)パラメーターオブジェクトによって提供できます。
  • 同じタイプのパラメーターが複数あるため、値交換エラーが発生する可能性が高くなります(ただし、この場合、メソッドと同じパラメーターリストを持つコンストラクターがある場合、パラメーターオブジェクトの方が適していません)。

パラメータオブジェクトは、呼び出し元と呼び出し先が依存する新しい依存関係を導入することは、独自の依存関係のない単純なクラスであるため、それほど不利ではありません。

したがって、パラメータオブジェクトは

  • 単一のパラメーターにはほとんど価値がなく、2つのパラメーターのメソッドには価値がある場合があります(たとえば、ポイントは通常x、yよりも優れています)。
  • より多くのメソッドが同じパラメーターリストを使用する場合に役立つ
32

Parameter Objectsは、relatedパラメーターをカプセル化して、メソッドまたはコンストラクターへのパラメーターの総数を減らすための素晴らしいアプローチを提供します。パラメータオブジェクトに実際に関連するパラメータが実際に含まれていることを確認するには、十分に注意する必要があります。

実際には、対処しているparameter typesに応じて、この問題に対処する方法は複数あります。複数のStringsやIntsのような一般的なタイプのパラメーターを処理していて、クライアントが実際に間違った引数のシーケンスを渡す可能性がある場合、多くの場合、それはより理にかなっていますcustom typesを作成します。可能な値を使用してenumを作成します。これにより、引数のコンパイル時間を適切にチェックできます。それらのもう1つの良い使用法は、関数からの複雑な値をreturnするために使用できることです。 こちら を参照してください。

私が多くの時間を費やしているもう1つのアプローチは、doStuffメソッドによって実行された作業が、依存関係の少ない単純なメソッドに分解されるかどうかを確認することです。

主に私はボブ・マーティンの最大3つのパラメーターの推奨に従うようにしています。まあ彼は実際にはそれは多くて1つだけであるべきだと言っています!何らかの増加は正当な理由があるはずです。この優れた本を参照してください: クリーンコード

7

AddressCurrentInvoiceを持つCustomerについて考えてみます。どちらが正しいですか-

SendInvoiceToAddress(Invoice invoice, Address adress);

または

SendInvoiceToAddress(Customer customer);

私は両方だと思います。または別の言い方をすると、それは実際にアプリに依存します。

すべての請求書が(定義により)1人の顧客に属している場合、それは1つのことです。そしてそれはあなたのメソッドがCustomerInvoiceSenderクラス(またはそのようなもの)の中に存在することを意味します。これは完全に顧客ドメイン内にあるものです。毎月、その月の請求書を送ります(それ以外は何もしません)。

複数の請求書を複数のアドレスに送信する必要がある場合(顧客のために、必ずしも目的ではない)、それはまったく別の話です。また、おそらくInvoiceSenderクラス(またはそのようなもの)内にも存在します。また、顧客ドメインとは何の関係もありません。この場合の顧客は、出荷される請求書の小さな1つのケースです。また、注目に値するのは、この場合、顧客の請求書ではなく具体的​​なインターフェースではなく、会社の請求書がたまたま共通のインターフェース(この場合は「いくつかのプロパティ」)を共有する2つのまったく異なるクラスである可能性があるということです。

1
LongChalk

DavidとSomの回答には、検討すべき優れた情報があります。以下を追加します。

多くの設計パターンと同様に、何をするかの決定は、オプションの長所と短所の連続性に依存します。常に正しい答えがあるとは限りません。多くの場合、どのプロの長所を楽しみたいか、どの短所を危険にさらすかが問題になります。

私の経験では、DTOに移動することは、関連の値が常に一緒に移動する場合に役立ちます。デビッドはこのアプローチの利点をよく説明しています。このアプローチで私が見たもう1つの欠点は、DTOが大きくなると、メソッドに不要な依存関係を追加するリスクがあることです。

たとえば、メソッドA、B、C、およびDはFoo、Bar、およびBazを取るので、これらの引数をDTOに組み合わせるのは素晴らしいことです。次に、メソッドAとBはQuuxを引き受ける必要があります-CおよびDに未使用の依存関係を引き受けるように強制するDUXにQuuxを追加しますか? CとDをテストするとき、どの値をQuuxに渡しますか?新しい開発者がメソッドCおよびDを使用する場合、Quxの存在により混乱が生じますか?方法AとCを比較するとき、Quxをどのように定義するべきか、または定義すべきでないかは明らかですか?

最初にすべてのメソッドにFoo、Bar、およびBazが必要であるが、一部のメソッドではこれらの値が不要になった場合も、同様の状況が発生します。

あるチームが別のチームのサービスにDTOを渡し、そのDTOに情報を正しく入力して同期するために多大な努力を払っていた経験を観察しました。

値が常に一緒にならない限り、混乱を引き起こし、テストの負担が増え、開発作業が増えるリスクがあります。値が常に一致する場合、DTOは明快さを提供し、重複を減らし、一貫性を単純化することができます。

1
Gordon Bean