パラメータの数が多すぎますか?[閉まっている]


228

ルーチンはパラメータを持つことができます、それはニュースではありません。必要な数のパラメーターを定義できますが、パラメーターが多すぎると、ルーチンの理解と保守が難しくなります。

もちろん、構造化変数を回避策として使用することもできます。これらの変数をすべて1つの構造体に入れて、それをルーチンに渡します。実際、構造を使用してパラメーターリストを簡略化することは、Steve McConnellがCode Completeで説明した手法の1つです。しかし彼が言うように:

慎重なプログラマーは、論理的に必要以上にデータをバンドルすることを避けます。

したがって、ルーチンのパラメーターが多すぎる場合、または構造体を使用して大きなパラメーターリストを偽装している場合は、おそらく何かが間違っています。つまり、カップリングを緩やかに保つのではありません。

私の質問は、パラメーターリストが大きすぎると見なすことができるのいつですか?5つを超えるパラメーターは多すぎると思います。どう思いますか?


1
ただ、ここで参照する実用的な例である。この問題 ...あまりにも多くされているどのように多くのパラメータを
ギデオン

5
JavaScriptの65537パラメータが多すぎます:jsfiddle.net/vhmgLkdm
Aadit M Shah

Apache HTTPコンポーネントを見るだけで、動的なものを構築することはほぼ不可能です
Andrew Scott Evans

回答:


162

言論の自由に対する第1改正の保証にもかかわらず、規制することができるほど卑猥と見なされるものはいつですか?ジャスティス・ポッター・スチュワートによると、「私はそれを見るとき、それを知っています」ここでも同じことが言えます。

プロジェクトのサイズやスコープによって答えが変わるだけでなく、モジュールレベルにまで変化するので、このように厳格で迅速なルールを作るのは嫌いです。メソッドが何をしているか、またはクラスが何を表現することになっているのかに応じて、2つの引数が多すぎて、結合が多すぎるという症状である可能性があります。

そもそも質問をして、あなたがしたのと同じくらいあなたの質問を修飾することによって、あなたはこれのすべてを本当に知っていることを提案します。ここでの最良の解決策は、厳密で高速な数値に依存するのではなく、同僚間のデザインレビューとコードレビューに目を向け、凝集度が低く、結合が密である領域を特定することです。

同僚に仕事を見せることを恐れないでください。あなたが恐れているなら、おそらくそれはあなたのコードに何か問題があり、あなたがすでにそれを知っているというより大きな兆候です。


良い経験則があるため、もはや、CPUレジスタの数であると、コンパイラは、スタック上にそれらを割り当てることを余儀なくされます。
Michaelangel007 2016年

1
コメントしている回答に関して@ Michaelangel007は、ルールがないことを示しているので、それを言ってはいけません。さらに、パラメーターの数は読みやすさの問題であり、パフォーマンスではありません。
clemp6r 2016

1
@ clemp6r不正解です- 両方です。コンパイラーは常に「レジスター・スピル」に対処する必要があります。唯一の公式の答えは、あなたのコンパイラが生成しているものの集合体を検査することです。レジスタの数は、同じプラットフォームで魔法のように変化しません。en.wikipedia.org/wiki/Register_allocation
Michaelangel007

124

一部のパラメーターが冗長である場合、関数はパラメーターが多すぎます。すべてのパラメーターを使用する場合、関数には正しい数のパラメーターが必要です。このよく使われる関数を見てみましょう:

HWND CreateWindowEx
(
  DWORD dwExStyle,
  LPCTSTR lpClassName,
  LPCTSTR lpWindowName,
  DWORD dwStyle,
  int x,
  int y,
  int nWidth,
  int nHeight,
  HWND hWndParent,
  HMENU hMenu,
  HINSTANCE hInstance,
  LPVOID lpParam
);

これは12個のパラメーター(x、y、w、hを長方形としてバンドルする場合は9個)であり、クラス名から派生したパラメーターもあります。これをどのように減らしますか?もう少しポイントを減らしたいですか?

パラメータの数を気にする必要はありません。論理的で十分に文書化されていることを確認し、インテリセンス*を使用してください。

*他のコーディングアシスタントが利用可能です!


37
私はこれに投票しています。他の「3」「4」の答えにはびっくり!正解は次のとおりです。最低限必要なものですが、かなりの数になることもあります。
Tony Andrews、

16
その機能が今日設計された場合、おそらく少し異なって見えるでしょう。x、y、nWidth、nHeightは、Rectangleオブジェクトにバンドルできます。styleとxStyleは、列挙型または文字列のセットに組み合わせることができます。これで、パラメーターは8つだけになりました。
finnw 2008年

16
@finnwその機能が今日設計されたのですか?すでに再設計されています。フォームf = new Form(); パラメータは0です。
Nick、

36
これはC winapiです。もっと悪い例は思いつきません。
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

17
いいえ、違います。これは手続き型です。ウィンドウはオブジェクトなので、これは現在廃止されています。今日、これは、パラメータの束ではなく、集合クラスで解決されます。優れたデザインの直観的なルールは、関数(パラメーターを含む)を1つの単純な文で記述できない場合、デザインが不十分であることです。私はこれが事実だと信じています。
JanTuroň12年

106

きれいなコード、ロバートC.マーティンは対象に4ページを捧げました。ここに要点があります:

関数の引数の理想的な数はゼロ(ニラディック)です。次に1つ(モナディック)、2つ(ダイアディック)がそれに続きます。3つの引数(3項)は可能な限り避けてください。3つ以上(ポリアディック)には、非常に特別な理由が必要です-とにかく使用しないでください。


2
「これ」は実行のコンテキストなので、これは含まれていません。関数型プログラミングでは、コンテキストはグローバルであり、おっと、コンテキストはメソッドを適用するオブジェクトです。また、パラメーターリストに「this」を含める場合、パラメーターを0にすることはできません(理想的)。
トム

1
いいえ、「これ」は含まれていません。
Patrick McElhaney、2008年

20
さらに、マーティンはC ++標準委員会に一言あるべきです。<algorithm>の半分は3つ以上のパラメーターを取ります。これは、1つの反復子の範囲がすでに2であるためです。これは、反復子を使用したプログラミング(つまり、STLコレクションを使用した汎用プログラミング)の性質にすぎません。
スティーブジェソップ

3
@SteveJessop:<algorithm>の欠点は、常にスライスで機能することです。アルゴリズムとコレクションを再設計する場合、アルゴリズムは常にコレクション全体を取得するようにします。これにより、コレクションのビューをスライスして、アルゴリズムがパーツで機能できるようになります。あるいは、一般的なケースでの作業を簡単にするために、省略オーバーライドも定義します。
Lie Ryan

3
@LieRyan:一方、私が再設計し<algorithm>ている場合は、範囲オブジェクトに影響を与えます。コレクションは範囲になりますが、すべての範囲がコレクションになるわけではありません。そして確かに、ブーストはすでにそれを行っています。とにかく、非常に広く使用されているライブラリはこのアドバイスを無視するというのが私のポイントです。そのため、もしあなたがそうした場合にもあなたに起こることが保証されている最悪のことは、何百万ものユーザーの多くがインターフェースを少し単純化していじくり回すことです;-)
スティーブジェソップ

79

私が過去に使用したいくつかのコードは、あまりにも多くのパラメーターを渡さないようにするためにグローバル変数を使用しました。

しないでください!

(通常。)


同じことを達成するために、使用されたクラスメンバーに取り組んでいたいくつかのコード。当時、私はCプログラマーでしたが、なぜグローバル変数が非常に多いのか、入出力を明示的にパラメーターにできないのはなぜですか?
user3528438 2015

38

シグネチャのパラメータを精神的に数えなくてはならず、それらを呼び出しに一致させる必要がある場合は、リファクタリングするときです!


それは非常に良い答えです。パラメータが論理的に構成されている場合(x、y、w、h)、すべてを正しい順序で覚えることは簡単です。特にfprintfが反対なので、FILEポインターをputc(パラメーターが2つしかない)のどこに置くかを覚えるのは難しいです。
user877329

最も良い答え。非常によく言われた
アンワル

31

すべての回答をありがとうございました。

  • 5つのパラメーターがコードの健全性にとって適切な制限であると(私のように)考えている人を見つけるのは少し意外でした。

  • 一般に、3から4までの制限が良い経験則であることに人々は同意する傾向があります。人々は通常4つ以上のものを数えるのに悪い時間を持っているので、これは合理的です。

  • 通りミラノポイント、平均の人に一度自分の頭の中で多かれ少なかれ7物事を保つことができます。しかし、あなたがルーチンを設計/維持/研究しているとき、あなたはパラメータだけより多くのことを心に留めなければならないことを忘れてはいけないと思います。

  • 一部の人々は、ルーチンは必要なだけの引数を持つべきだと考えています。私は同意しますが、いくつかの特定のケース(OS APIの呼び出し、最適化が重要なルーチンなど)についてのみです。可能な限り、これらの呼び出しのすぐ上に抽象化の層を追加して、これらのルーチンの複雑さを隠すことをお勧めします。

  • ニックはこれについていくつかの興味深い考え持っています。彼のコメントを読みたくない場合は、要約します。簡単に言えば、次のように異なります

    プロジェクトのサイズやスコープによって答えが変わるだけでなく、モジュールレベルにまで変化するので、このように厳格で迅速なルールを作るのは嫌いです。メソッドが何をしているか、またはクラスが何を表現することになっているのかに応じて、2つの引数が多すぎて、結合が多すぎるという症状である可能性があります。

    ここでの教訓は、コードを同僚に示すことを恐れないでください。同僚と話し合って、「凝集度が低く、結合が密になっている領域を特定するようにしてください。

  • 最後に、私はwnoiseがNickに大いに同意し、プログラミング芸術のこの詩的なビジョン(以下のコメントを参照)による彼の風刺的な貢献を結論付けます。

    プログラミングはエンジニアリングではありません。コードの編成は、ハードルールのコンテキストに依存しすぎる人的要因に依存するため、芸術です。


16

この回答はオブジェクト指向言語を前提としています。使用していない場合は、この回答をスキップしてください(言い換えれば、これは言語に依存しない回答ではありません)。

3つ以上のパラメーター(特に組み込み型/オブジェクト)を渡す場合、それが「多すぎる」ことではなく、新しいオブジェクトを作成する機会を逃している可能性があります。

複数のメソッドに渡されるパラメーターのグループを探します。2つのメソッドに渡されるグループであっても、新しいオブジェクトが存在することがほぼ保証されます。

次に、機能を新しいオブジェクトにリファクタリングし、それがコードとオブジェクト指向プログラミングの理解の両方にどれだけ役立つかを信じません。


ルーチンやパラメータを使用しない言語を指定してください。アセンブリはルーチン(ラベル)を持っていると考えることもできます。
Auron、

x86アセンブリには、必ずルーチン(呼び出し/戻り)があります。その他には、cmp / jmpコンボが必要な場合があります。
Brian Knoblauch、

申し訳ありませんが、「戻る」ではなく「ret」です。ため息。もう長い一日だった。
Brian Knoblauch、

あいまいな表現のAuronについて申し訳ありませんが、私はそれを修正したと思います。質問ではなく、言語固有の回答でした。
ビルK

1
すべての言語で構造体を渡すことができるわけではありません。また、構造体を渡すことは、受信メソッドが構造体を処理するためのコードを持っている必要があるため、より多くのパラメーターが必要になる場合があることを意味します。また、非OO言語では、通常、追加のパラメーターが必要です。すべてのOO言語には、「this」という「非表示」パラメーターがあり、それ以外の場合は渡す必要があります(または、より恐ろしい言語/デザインでは、グローバルになる可能性があります。アクセス可能)
ビルK

13

単なる数以外の考慮事項があるようですが、ここで頭に浮かぶものがいくつかあります。

  1. 機能の主な目的と一時的な設定との論理的な関係

  2. それらが単に環境フラグである場合、バンドルは非常に便利です。


12

Alan Perlisの有名なプログラミングエピグラム(1982年9月のACM SIGPLAN Notices 17(9)で詳述)の1つは、「10個のパラメーターを持つプロシージャがある場合、おそらくいくつかを見落としている」と述べています。



9

私にとって、リストがIDEの1行と交差する場合、1つのパラメーターが多すぎます。アイコンタクトを壊すことなく、すべてのパラメータを1行で表示したい。しかし、それは私の個人的な好みにすぎません。


これは、一部の開発者がfoo(int a、float b、string c、double d)を試すまでは問題ありません。私は彼らと一緒に仕事をしないようにしようとするのが最善だと思います。:D
Rontologist 2008年

4
他の誰かが途方もなく長い名前をクラスに付けた場合、それが私がルーチンを定義または呼び出す方法に影響を与えないようにします。
finnw 2008年

9
「復帰」を紹介してもらえますか?

3
リストがIDEの1行と交差する場合、モニターが小さすぎてそのコードで使用できないため、水平解像度が高いモニターを購入する必要があります。改行またはパラメータの量の削減は、根本的な問題を解決しない単なる回避策です。つまり、モニターがコードベースに対して小さすぎるということです。
カイザールディ2015

9

私は一般的に5に同意しますが、さらに必要な状況があり、それが問題を解決する最も明確な方法である場合は、さらに使用します。


8

短期記憶の7つのこと?

  1. 関数の名前
  2. 関数の戻り値
  3. 機能の目的
  4. パラメータ1
  5. パラメータ2
  6. パラメータ3
  7. パラメータ4

上手。それは経験則です。関数の本体をコーディングするときは、その名前を気にする必要はありません。戻り値とその目的は密接に関連しています。
Auron、

7
8.パラメータの順序
2013年

7

最悪の5コードスニペット、2つ目の「これはコンストラクタです」を確認してください。37⋅4≈150以上のパラメーターがあります。

ここでプログラマーがこのコンストラクターを書きました[... S]はい、それは大きなコンストラクターだと思うかもしれませんが、Eclipse自動コード生成ツールを使用しました。このコンストラクタは手動で作成されたと結論付けます。(ちなみに、これはコンストラクタの上部にすぎず、完全ではありません)。

150以上のパラメーターを持つコンストラクター


これはとても悲しいことです。複数の値をまとめてそれらに共通の名前を付け、読みやすい方法でそれらの多くを階層的に表すことができる「レコード」、「構造体」、または「値オブジェクト」について知らない誰もあなたも🙈ことを行うことができますあなたに告げないため、年間のunlaced靴
ヨーマン

6

必要以上に1つ。私はglibになるつもりはありませんが、いくつかのオプションを必要とする関数がいくつかあります。例えば:

void *
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t offset);

議論は6つあり、どれもが不可欠です。さらに、それらをバンドルすることを正当化するためのそれらの間の共通のリンクはありません。「struct mmapargs」を定義できるかもしれませんが、それはさらに悪くなるでしょう。


まあ、protとすることはflags、それが5何とかもっと良い方法のように6. Aビット以上のマジックナンバーだったことを設計者が感じられた場合には一緒に巻かれている可能性がopen他のすべての雑多なフラグを読み取り/書き込みモードを兼ね備えています。そして、マップされたセクションがの現在のシーク位置で始まるように指定することで、おそらく取り除くことができます。リージョンにアクセスできない状況があるかどうかはわかりませんが、そうでない場合は厳密に必要というわけではありません。offsetfiledesmmaplseek
スティーブジェソップ2013

...したがってmmap、デザイナーとユーザーによってはパラメーターの長いリストを好む一方で、呼び出しを行う前に少数のパラメーターを準備するためにいくつかのステップを実行することを好む人がいるという事実をよく表しています。
スティーブジェソップ2013

1
@Steve:別の呼び出しでシーク位置を事前に設定すると、不当な競合状態が発生します。一部のAPI(OpenGL)では、呼び出しに影響を与えるパラメーターが多すぎて実際に状態を使用する必要がありますが、通常、すべての呼び出しは可能な限り独立している必要があります。離れた場所での行動は、ダークサイドへの道です。
Ben Voigt 2015

5

Perlのベストプラクティスによると、3は大丈夫、4は多すぎます。あくまでも目安ですが、当店ではこだわり続けています。


5

私は自分で5つのパラメーターでパブリック関数の制限を描画します。

私見、長いパラメーターリストは、コード内の特定の場所からのみ呼び出されることを意図したプライベート/ローカルヘルパー関数でのみ使用できます。これらの場合、多くの状態情報を渡す必要があるかもしれませんが、あなた(またはコードを保守し、モジュールの基本を理解している人)だけが気にする必要があるため、読みやすさはそれほど重要ではありません。その関数を呼び出す。


この時点で、多くの人が実際の議論ではなくすべてのことを行う理由は、まさにそのためです。状態は持ち越され、フィールド(メンバー変数)の形のオブジェクトの状態だけです。そのオブジェクトはクラスとして表されます。これは、1回またはすべての使用に対してインスタンス化されます。このようなオブジェクトには、パッケージのプライベートメソッドまたはパブリックメソッドが1つしかない場合があります。これらのメソッドは、引数をそれぞれ持たないプライベートメソッドに処理を委任します。構成と状態が適切な場所にあるため、いくつかの引数が可能になりました:)
yeoman

5

あなたが考慮しなければならない関連する質問は、ルーチンがどれほどまとまりがあるかです。パラメータの数が多いと、ルーチン自体がやりすぎているため、まとまりが疑われることがわかります。ハードで高速なパラメーターの数はおそらく不可能であることに同意しますが、凝集度の高いルーチンはパラメーターの数が少ないことを意味すると推測します。



4

一般的な経験則として、3つのパラメーターで停止します。今後は、代わりにパラメーターの配列または構成オブジェクトを渡す必要があります。これにより、APIを変更せずに将来のパラメーターを追加することもできます。


APIが変更された場合、APIは実際に変更されるはずです。非互換性が依然として発生する可能性があるステルスの変更があるだけでなく、あまり明確ではありません。
wnoise

あなたはエッジケースを設定するための1つの以上のパラメータが必要な場合は、それがAPIを使用して非関連コンポーネントに伝播してはならない
エランGalperin

4

パラメータリストの長さ制限は、もう1つ制限です。そして、制限は暴力の適用を意味します。面白そうに聞こえますが、プログラミングをしているときでも、暴力的ではないかもしれません。コードにルールを指示させるだけです。多くのパラメーターがある場合、関数/クラスメソッドの本体がそれらを利用するのに十分な大きさになることは明らかです。また、大きなコードスニペットは通常、リファクタリングして小さなチャンクに分割できます。つまり、リファクタリングされた小さなコードに分割されるため、多くのパラメーターを無料ボーナスとして使用することに対する解決策が得られます。


4

パフォーマンスの観点から指摘しておきたいのは、メソッドへのパラメーターの渡し方によっては、値を指定して多数のパラメーターを渡すと、各パラメーターをコピーしてスタックに配置する必要があるため、プログラムの速度が低下するということです。

単一のクラスを使用してすべてのパラメーターを包含すると、参照によって渡される単一のパラメーターがエレガントでクリーンで高速になるため、より効果的に機能します。


私はこの回答に+1を与えています。これは、主観的なものであるコードのクリーン度または任意の制限以外のすべてについて議論する唯一の回答だからです。一部の人々は、ループに入ってスタックの内外で何十もの引数をプッシュしているときに、スタックに対して何をしているのか考えないかもしれません。ループ内にある場合は、コンパイル対象のABIでREGISTERSによって渡される引数の数を使用することを検討する必要があります。たとえば、MS x64 ABIでは、レジスタを介して渡される最大引数は4です。「Windows以外のOSで使用される」「システムV」ABIはより多くのレジスタを使用するため、4つの引数を使用するとかなり移植性が高くなります
Lakey

3

私によると、あなたが4またはいくつかの固定数を超える場合があるかもしれません。気をつけるべきことは

  1. メソッドが多すぎるため、リファクタリングする必要があります。
  2. コレクションまたはデータ構造の使用を検討する必要がある場合があります。
  3. クラスの設計を考え直してください。おそらく、渡す必要のないものもあります。

使いやすさやコードの読みやすさの観点から、メソッドシグネチャを「ワードラップ」する必要がある場合は、停止して考える必要があると思います。検索結果はありません。過去と現在のいくつかの非常に優れたライブラリーは、4-5個以上のPRAMを使用しています。


3

私の経験則では、コールを見て、それが何をするかを伝えるのに十分長い間、パラメータを記憶できる必要があります。したがって、メソッドを確認できず、メソッドの呼び出しに切り替えて、どのパラメーターが何を実行するかを覚えている場合は、数が多すぎます。

私にとってそれは約5に相当しますが、私はそれほど明るくありません。あなたのマイレージは異なる場合があります。

パラメータを保持するプロパティを持つオブジェクトを作成し、設定した制限を超えた場合に渡すことができます。Martin Fowlerのリファクタリングの本と、メソッド呼び出しをより簡単にすることに関する章を参照してください。


1

それはあなたが働いている環境に大きく依存します。例えばJavaScriptを見てください。JavaScriptでは、パラメータを渡す最善の方法は、キーと値のペアを持つオブジェクトを使用することです。つまり、実際には1つのパラメータしかありません。他のシステムでは、スイートスポットは3または4になります。

結局、それはすべて個人の好みに要約されます。


1

3は大丈夫、4はガイドラインとして多すぎることに同意します。3つ以上のパラメーターを使用すると、必然的に複数のタスクを実行することになります。複数のタスクを別々のメソッドに分割する必要があります。

しかし、私が取り組んだ最新のプロジェクトを見ると、例外がたくさんあり、ほとんどの場合、3つのパラメーターに絞ることは困難です。


1

私は新しいクラスにそれらを束ねる時に1つのルーチンIルックで7-10のパラメータを持っている場合しかし、そのクラスが何もなく、ゲッターとセッターを持つフィールドの束ないだろうされていない場合-新しいクラスをしているでシャッフル値以外の何かをし、でる。そうでなければ、私はむしろ長いパラメータリストを我慢したいと思います。


1
複数の場所で使用される場合は、データのみのクラスにバンドルしますが、それでも通常は両方のコンストラクタを作成します。
Leahn Novash 2009

1

平均して、人々は一度に頭に7 +/- 2ものを保持できることが知られている事実です。私はその原則をパラメーターで使用するのが好きです。プログラマーがすべて平均以上のインテリジェントな人々であると仮定すると、10歳以上は多すぎると思います。

ところで、パラメータが何らかの形で似ている場合は、構造体やクラスではなく、ベクトルやリストに入れます。


1

関数の呼び出し頻度に基づいて答えを出します。

これが一度しか呼び出されないinit関数である場合は、気にかける人が10回以上かかるようにします。

フレームごとの束と呼ばれる場合は、構造体を作成し、ポインタを渡す傾向があります。これは高速になる傾向があるためです(構造体を毎回再構築しない場合を想定しています)。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.