私が以下を持っていると仮定します
_Class A {
Foo getFoo();
Bar getBar();
Baz getBaz();
}
_
そして、doStuff
、Foo
、Bar
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つの方法の長所と短所に関する追加の洞察を共有できますか?
メソッドA(裸のパラメーター)には常に次の利点があります。
メソッドB( パラメータオブジェクト )には、次のような利点があります。
パラメータオブジェクトは、呼び出し元と呼び出し先が依存する新しい依存関係を導入することは、独自の依存関係のない単純なクラスであるため、それほど不利ではありません。
したがって、パラメータオブジェクトは
Parameter Object
sは、related
パラメーターをカプセル化して、メソッドまたはコンストラクターへのパラメーターの総数を減らすための素晴らしいアプローチを提供します。パラメータオブジェクトに実際に関連するパラメータが実際に含まれていることを確認するには、十分に注意する必要があります。
実際には、対処しているparameter types
に応じて、この問題に対処する方法は複数あります。複数のString
sやInt
sのような一般的なタイプのパラメーターを処理していて、クライアントが実際に間違った引数のシーケンスを渡す可能性がある場合、多くの場合、それはより理にかなっていますcustom types
を作成します。可能な値を使用してenum
を作成します。これにより、引数のコンパイル時間を適切にチェックできます。それらのもう1つの良い使用法は、関数からの複雑な値をreturn
するために使用できることです。 こちら を参照してください。
私が多くの時間を費やしているもう1つのアプローチは、doStuff
メソッドによって実行された作業が、依存関係の少ない単純なメソッドに分解されるかどうかを確認することです。
主に私はボブ・マーティンの最大3つのパラメーターの推奨に従うようにしています。まあ彼は実際にはそれは多くて1つだけであるべきだと言っています!何らかの増加は正当な理由があるはずです。この優れた本を参照してください: クリーンコード
AddressとCurrentInvoiceを持つCustomerについて考えてみます。どちらが正しいですか-
SendInvoiceToAddress(Invoice invoice, Address adress);
または
SendInvoiceToAddress(Customer customer);
私は両方だと思います。または別の言い方をすると、それは実際にアプリに依存します。
すべての請求書が(定義により)1人の顧客に属している場合、それは1つのことです。そしてそれはあなたのメソッドがCustomerInvoiceSenderクラス(またはそのようなもの)の中に存在することを意味します。これは完全に顧客ドメイン内にあるものです。毎月、その月の請求書を送ります(それ以外は何もしません)。
複数の請求書を複数のアドレスに送信する必要がある場合(顧客のために、必ずしも目的ではない)、それはまったく別の話です。また、おそらくInvoiceSenderクラス(またはそのようなもの)内にも存在します。また、顧客ドメインとは何の関係もありません。この場合の顧客は、出荷される請求書の小さな1つのケースです。また、注目に値するのは、この場合、顧客の請求書ではなく具体的なインターフェースではなく、会社の請求書がたまたま共通のインターフェース(この場合は「いくつかのプロパティ」)を共有する2つのまったく異なるクラスである可能性があるということです。
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は明快さを提供し、重複を減らし、一貫性を単純化することができます。