多くの引数を持つコンストラクターの回避


10

だから私は異なるクラスのオブジェクトを作成するファクトリーを持っています。可能なクラスはすべて抽象祖先から派生しています。ファクトリーには構成ファイル(JSON構文)があり、ユーザーの構成に応じて、作成するクラスを決定します。

これを実現するために、ファクトリはJSON解析にboost :: property_treeを使用します。彼はptreeをウォークスルーし、どの具象オブジェクトを作成するかを決定します。

ただし、製品オブジェクトには多くのフィールド(属性)があります。具体的なクラスにもよりますが、オブジェクトには約5〜10個の属性があり、将来的にはさらに増える可能性があります。

そのため、オブジェクトのコンストラクターがどのように見えるかわかりません。私は2つの解決策を考えることができます:

1)製品のコンストラクターはすべての属性をパラメーターとして想定しているため、コンストラクターは最終的に10個以上のパラメーターになります。これは醜く、長くて読めないコード行につながります。ただし、利点は、ファクトリーがJSONを解析し、正しいパラメーターでコンストラクターを呼び出すことができることです。製品クラスは、JSON構成のために作成されたことを知る必要はありません。JSONや設定が含まれていることを知る必要はありません。

2)製品のコンストラクターは、property_treeオブジェクトという1つの引数のみを想定しています。次に、必要な情報を解析できます。構成内の情報が欠落しているか範囲外の場合、各製品クラスは適切に対応できます。ファクトリは、いくつかの製品に必要な引数を知る必要はありません。ファクトリーは、誤った構成の場合の対応方法を知る必要もありません。また、コンストラクターインターフェイスは統一されており、小さいです。ただし、デメリットとして、製品は必要な情報をJSONから抽出する必要があるため、JSONがどのように構成されているかを認識しています。

私は解決策2)を好む傾向があります。ただし、これが適切なファクトリパターンであるかどうかはわかりません。JSON構成で作成されていることを製品に通知するのは、どういうわけか間違っていると感じます。一方、新製品は非常に簡単に導入できます。

それについての意見はありますか?



1
私はあなたのリンクをたどりました。ラチェットフリークからの最高評価の回答に例があります。しかし、この「ビルダー」アプローチはどのような問題を解決しますか?コードラインDataClass data = builder.createResult();があります。ただし、createResults()メソッドは引き続き10個のパラメーターをDataClassオブジェクトに取得する必要があります。しかし、どうやって?抽象化の層がもう1つあるようですが、DataClassのコンストラクターは小さくなりません。
lugge86

ビルダーとプロトタイプを見てください。
Silviu Burcea

Silviu Burcea、私がやった。ただし、ビルダーを使用する場合、ビルダーはどのようにしてパラメーターを製品に取得しますか?どこかに太っているインターフェイスでなければなりません。ビルダーはもう1つのレイヤーにすぎませんが、どういうわけか、パラメーターは製品クラスへの経路を見つける必要があります。
lugge86

1
クラスが大きすぎる場合、コンストラクターの引数を移動しても、大きすぎません
Telastyn

回答:


10

私はオプション2を実行しません。それは、ブーストプロパティツリーの解析を使用してオブジェクトの構築を永遠に畳み込んでいるためです。これだけ多くのパラメーターを必要とするクラスに慣れている場合は、そのような多くのパラメーターを必要とするコンストラクターに慣れる必要があります。

コードの可読性が主な懸念事項である場合は、ビルダーパターンを使用できます。これは基本的に、名前付き引数がないためのc ++ / javaストップギャップです。最終的には次のようになります。

MyObject o = MyObject::Builder()
               .setParam1(val1)
               .setParam2(val2)
               .setParam3(val3)
             .build();

そのため、MyObjectには、Builder :: buildで呼び出されるプライベートコンストラクターがあります。すばらしいのは、10個のパラメーターを持つコンストラクターを呼び出さなければならない唯一の場所であることです。ブーストプロパティツリーファクトリはビルダーを使用します。その後、MyObjectを直接または別のソースから構築する場合は、ビルダーを使用します。そしてビルダーは基本的に、各パラメーターを渡すときに明確に名前を付けることができるため、より読みやすくなっています。これは明らかにいくつかのボイラープレートを追加するので、乱雑なコンストラクターを呼び出すか、既存のパラメーターのいくつかを構造体にまとめるなどと比較して、それが価値があるかどうかを判断する必要があります。テーブルに別のオプションを投げるだけです。

https://en.wikipedia.org/wiki/Builder_pattern#C.2B.2B_Example


5

2番目の方法は使用しないでください。

これは間違いなく解決策ではなく、ファクトリーがあるアプリの部分ではなく、ビジネスロジックでクラスをインスタンス化するだけです。

どちらか:

  • 同じようなものを表すように見える特定のパラメータをオブジェクトにグループ化しようとする
  • 現在のクラスをいくつかの小さなクラスに分割します(10個のパラメーターを持つサービスクラスを持っていると、クラスが多くのことをしているように見えます)
  • クラスが実際にはサービスではなく、代わりに値オブジェクトである場合は、そのままにします

作成するオブジェクトが実際にデータの保持を担当するクラスでない限り、コードをリファクタリングして大きなクラスを小さなクラスに分割する必要があります。


まあ、ビジネスロジックは製品を返すファクトリを使用しているため、ビジネスロジックはJSON / ptreeのものを参照しません。しかし、パーサーのコードをconstrucotrに含めるのは間違っていると感じています。
lugge86

クラスは組み込みシステムのGUIでウィジェットを表しているため、5つ以上の属性(x_coord、y_coord、backgroundcolor、framesize、framecolor、text ...)は私にとっては問題ないように見えます
lugge86

1
@ lugge86ファクトリを使用してJSONを解析しているためnew、ビジネスロジック内でのオブジェクトの呼び出しや構築を回避しているにもかかわらず、あまり良い設計ではありません。MiškoHeveryによる話を探していけないことを確認してください。MiškoHeveryが、テストと読み取りの両方の観点から、工場のアプローチがなぜ悪いのかをより深く説明しています。また、クラスはデータオブジェクトのように見えますが、それらの場合は通常、通常のサービスクラスよりも多くのパラメーターを指定しても問題ありません。あまり気になりません。
アンディ

私は工場での取り組みに満足していますが、あなたのリンクをたどって考えます。ただし、このトピックではファクトリは問題ではありません。問題は、まだ製品をセットアップする方法です...
lugge86

「10個のパラメーターを持つサービスクラスがあると、クラスが多くのことを行うように見えます」機械学習ではそうではありません。どのMLアルゴリズムにも、大量の調整可能なパラメーターがあります。MLをコーディングするときに、これに対処する正しい方法は何でしょうか。
Siyuan Ren

0

オプション2はほぼ正しいです。

改善されたオプション2

JSON構造のオブジェクトを取得してビットを取り出し、ファクトリーコンストラクターを呼び出すのが「正面向き」のクラスです。それは工場が作るものを取り、それをクライアントに渡します。

  • ファクトリーは、そのようなJSONが存在することさえまったく知りません。
  • クライアントは、ファクトリが必要とする特定のビットを知る必要はありません。

基本的に「フロントエンド」は2人のボブに言います「私は編集された顧客に対処するので、エンジニアは必要ありません。貧しいトム。彼が「私はクライアントを建設から切り離しました。この結果は非常にまとまりのある工場です」と言っただけなら; 彼は仕事を続けたかもしれない。

あまりにも多くの引数?

クライアント向けではありません-フロントエンド通信。

フロントエンド-工場?10個のパラメーターでない場合は、アンパックを延期することをお勧めします。元のJSONでない場合は、DTOを使用します。これは、JSONをファクトリーに渡すよりも優れていますか?私が言うのと同じ違い。

個々のパラメーターを渡すことを強く検討します。クリーンでまとまりのある工場という目標に固執する。@DavidPacker回答の懸念を回避します。

「引数が多すぎます」を軽減する

  • ファクトリまたはクラスコンストラクター

    • 特定のクラス/オブジェクト構築の引数のみを取る。
    • デフォルトのパラメータ
    • オプションのパラメーター
    • 名前付き引数
  • フロントエンド引数のグループ化

    • 上記のコンストラクターシグネチャによって導かれた引数値を調べ、評価し、検証し、設定します。

「ファクトリーは、そのようなJSONが存在することさえまったく考えていません」-では、ファクトリーは何のためのものですか?製品と消費者の両方から製品作成の詳細を隠します。なぜさらに別のクラスが役立つのですか?私は、JSONを話す工場で大丈夫です。XMLを解析するための別のファクトリーを実装し、将来的には「抽象ファクトリー」を実装することができます...
lugge86

ファクトリー氏:「あなたが望むオブジェクトは何ですか?...それは何ですか?どのクラスオブジェクトを構築するか教えてください。」ボブおじさんが「実装の詳細だ」と言っているように、構成ファイルJSONはデータソースです。それは別のソースから、および/または別の形式でできます。一般的に、特定のデータソースの詳細から切り離したいと考えています。ソースまたはフォームが変更されても、ファクトリは変更されません。ソース+パーサーと、分離モジュールとしてのファクトリーを指定すると、両方が再利用可能になります。
radarbob
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.