パラメーターが多すぎますか? [閉まっている]
-
05-07-2019 - |
質問
ルーチンはパラメーターを持つことができますが、それはニュースではありません。必要な数のパラメーターを定義できますが、パラメーターが多すぎると、ルーチンの理解と保守が困難になります。
もちろん、回避策として構造化変数を使用できます。これらすべての変数を単一の構造体に入れて、ルーチンに渡します。実際、構造を使用してパラメーターリストを単純化することは、スティーブマッコネルが Code Complete で説明した手法の1つです。しかし、彼が言うように:
慎重なプログラマーは、論理的に必要な以上のデータのバンドルを避けます。
したがって、ルーチンのパラメーターが多すぎる場合、または構造体を使用して大きなパラメーターリストを偽装している場合、おそらく何か間違ったことをしていることになります。つまり、カップリングを緩めたままにしていません。
質問は、パラメータリストが大きすぎると考えられるのはいつですか? 5つ以上のパラメータが多すぎると思います。どう思いますか?
解決
言論の自由に対する修正第1条の保証にもかかわらず、規制できるものとして卑obであると考えられるものはいつですか?ポッター・スチュワート判事によると、「私はそれを見たときにそれを知っています」と述べています。ここでも同じことが言えます。
プロジェクトのサイズや範囲によって答えが変わるだけでなく、モジュールレベルまでも変わるので、このような厳格なルールを作成するのは嫌です。メソッドが何をしているか、またはクラスが何を表すかによって、2つの引数が多すぎて、カップリングが多すぎることを示す可能性があります。
そもそも質問をして、あなたと同じくらいあなたの質問を限定することによって、あなたはこのすべてを本当に知っていることをお勧めします。ここでの最善の解決策は、ハードで速い数字に頼るのではなく、同僚の間でデザインレビューとコードレビューに目を向けて、凝集度が低く密結合のある領域を特定することです。
同僚に自分の作品を見せることを恐れないでください。もしあなたが恐れているなら、それはおそらくあなたのコードに何か問題があること、そしてあなたが既にそれを知っているという大きな兆候です。
他のヒント
一部のパラメーターが冗長である場合、関数にはパラメーターが多すぎます。すべてのパラメーターを使用する場合、関数には正しい数のパラメーターが必要です。このよく使用される関数をご覧ください:
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個)であり、クラス名から派生したパラメーターもあります。これをどのように減らしますか?数をもっとポイントまで減らしたいですか?
パラメーターの数を気にせずに、論理的で十分に文書化されていることを確認し、intellisense * があなたを助けるようにしてください。
* 他のコーディングアシスタントが利用可能です!
クリーンコードでは、ロバートC.マーティンがこのテーマに4ページを捧げました。 。要点は次のとおりです。
aの引数の理想的な数 関数はゼロ(niladic)です。次に来る 1(単項)、その後に密接に2 (ダイアディック)。 3つの引数(3項) 可能な場合は避けてください。もっと 3つ(ポリアド)よりも非常に必要です 特別な正当化-そしてその後 とにかく使用すべきではありません。
過去に使用したコードでは、あまりにも多くのパラメーターを渡さないようにするためにグローバル変数を使用しました。
そうしないでください!
(通常。)
署名のパラメーターを暗記して、呼び出しに一致させなければならない場合は、リファクタリングの時間です!
ご回答ありがとうございます。
-
5個のパラメーターがコードの健全性の適切な制限であると考える人を見つけるのは少し驚くべきことでした(
-
一般に、人々は3〜4の制限が大まかな目安であることに同意する傾向があります。人々は通常4つ以上のものを数えるのに苦労するため、これは合理的です。
-
ミラノポイント、平均して、人は一度に7つ以上の物を頭に留めることができます。しかし、ルーチンを設計/保守/研究するときは、パラメータだけでなく、もっと多くのことを覚えておく必要があることを忘れることはできないと思います。
-
一部の人々は、ルーチンは必要なだけの引数を持つべきだと考えています。私は同意しますが、いくつかの特定のケース(OS APIへの呼び出し、最適化が重要なルーチンなど)についてのみです。これらの呼び出しのすぐ上に可能な限り抽象化層を追加することにより、これらのルーチンの複雑さを隠すことをお勧めします。
-
Nick には興味深い考えがあります彼のコメントを読みたくない場合、私はあなたのために要約します:一言で言えば、それは依存します:
プロジェクトのサイズや範囲によって答えが変わるだけでなく、モジュールレベルまでも変わるので、このような厳格なルールを作成するのは嫌です。メソッドが何をしているか、またはクラスが何を表すかによって、2つの引数が多すぎて、カップリングが多すぎることを示す可能性があります。
ここでの教訓は、コードを同僚に見せることを恐れず、同僚と話し合い、"密着度が低く、密結合のある領域を特定すること" を試みることです。
> -
最後に、 wnoise はNickと大いに同意し、プログラミングの芸術のこの詩的なビジョン(下記のコメントを参照):
プログラミングはエンジニアリングではありません。コードの編成は、人間の要因に依存しているため、芸術です。人的要因は、厳格なルールではコンテキストに依存しすぎています。
この回答では、OO言語を想定しています。 1つを使用していない場合は、この回答をスキップしてください(これは、言語にとらわれない答えではありません。
3つ以上のパラメーター(特に組み込み型/オブジェクト)を渡す場合、「多すぎる」ということではありません。ただし、新しいオブジェクトを作成する機会を逃している可能性があります。
複数のメソッドに渡されるパラメーターのグループを探します-2つのメソッドに渡されたグループでさえ、そこに新しいオブジェクトがあることをほぼ保証します。
次に、機能を新しいオブジェクトにリファクタリングしますが、それがコードとオブジェクト指向プログラミングの理解の両方にどれほど役立つかは信じられません。
単なる数字以外の考慮事項があるように思われますが、ここで思い浮かぶものがいくつかあります:
-
関数の主な目的と一時的な設定との論理的な関係
-
それらが単なる環境フラグである場合、バンドルは非常に便利です
Alan Perlisの有名なプログラミングエピグラムの1つ(ACM SIGPLAN Notices 17(9)、1982年9月)には、「10個のパラメーターを持つプロシージャがある場合、おそらくいくつかを見逃した」と記載されています。
コード完了のSteve McConnellによると、
ルーチンの数を制限する 約7つのパラメーター
私にとって、リストがIDEで1行にまたがると、1つのパラメーターが多すぎます。アイコンタクトを壊すことなく、すべてのパラメーターを1行で表示したい。しかし、それは私の個人的な好みです。
一般に5に同意しますが、さらに必要な状況があり、それが問題を解決する最も明確な方法である場合は、さらに使用します。
短期記憶にある7つのもの?
- 関数の名前
- 関数の戻り値
- 関数の目的
- パラメーター1
- パラメーター2
- パラメーター3
- パラメーター4
Worst 5 Code Snippets 、2つ目の「これはコンストラクタですか」を確認してください。 37以上のようなものがあります⋅ 4≈ 150個のパラメーター:
ここでプログラマーがこのコンストラクターを書いた[... S]はい、それは大きなコンストラクターだと思う人もいるかもしれませんが、彼はEclipse自動コード生成ツールを使用しました[。] NOO、このコンストラクターには小さなバグがあり、このコンストラクターは手書きで書かれていると結論付けました。 (ちなみに、これはコンストラクターの最上部にすぎず、完全ではありません。)
必要以上に1つ。私はglibになるつもりはありませんが、かなり多くのオプションを必要とする機能がいくつかあります。例:
void *
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t offset);
6つの引数があり、それらはすべて必須です。さらに、それらを束ねる正当な理由はありません。 「struct mmapargs」を定義することもできますが、それはさらに悪いことです。
Perlベストプラクティスによれば、3は大丈夫、4は多すぎます。それは単なるガイドラインですが、私たちの店ではそれを固守しようとしています。
自分で5つのパラメーターでパブリック関数の制限を描画します。
IMHO、長いパラメーターリストは、コード内のいくつかの特定の場所からのみ呼び出されることを意図したプライベート/ローカルヘルパー関数でのみ受け入れられます。そのような場合、多くの状態情報を渡す必要がありますが、読みやすさはそれほど重要ではありません。なぜなら、あなた(またはコードを維持し、モジュールの基本を理解する必要がある人)だけが気にしなければならないからです。その関数を呼び出します。
考慮すべき関連する質問は、ルーチンが凝集性である方法です。多数のパラメーターは、ルーチン自体がやりすぎているため、凝集性が疑われることを示す匂いかもしれません。ハードで速い数のパラメーターはおそらく不可能であることに同意しますが、凝集度の高いルーチンはパラメーターの数が少ないことを意味すると思います。
少なすぎると柔軟性が失われます。
一般的な経験則として、3つのパラメーターで停止します。これ以上ではなく、代わりにパラメーターの配列または構成オブジェクトを渡します。これにより、APIを変更せずに将来のパラメーターを追加することもできます。
パラメータリストの長さ制限は、もう1つの制限です。そして、制限は暴力の適用を意味します。面白そうに聞こえますが、プログラミングをしているときでさえ暴力的ではない場合があります。コードにルールを指示させてください。多くのパラメータがある場合、関数/クラスメソッドの本体はそれらを利用するのに十分な大きさになることは明らかです。そして、通常、大きなコードスニペットはリファクタリングされ、小さなチャンクに分割されます。リファクタリングされた小さなコードに分割されるため、多くのパラメーターを無料ボーナスとして使用することに対する解決策が得られます。
パフォーマンスの観点から指摘することの1つは、パラメーターをメソッドに渡す方法によっては、多くのパラメーターを値で渡すと、各パラメーターをコピーしてスタックに配置する必要があるため、プログラムの速度が低下することです。
単一のクラスを使用してすべてのパラメーターを含めると、参照によって渡される単一のパラメーターがエレガントでクリーンで高速になるため、より適切に動作します!
私によると、4かいくつかの固定数を超える場合があります。 注目すべきことは
- メソッドの処理が多すぎるため、リファクタリングする必要があります。
- コレクションまたは何らかのデータ構造の使用を検討することもできます。
- クラスの設計を再考します。おそらく、いくつかのことをやり直す必要はありません。
使いやすさやコードの読みやすさの観点から、「ワードラップ」をちょっと必要とするときは、あなたのメソッドの署名は、あなたが無力だと感じ、署名を小さくするためのすべての努力が結果にならない限り、停止して考える必要があります。過去および現在の非常に優れたライブラリには、4〜5台以上の乳母車が使用されています。
私の経験則では、呼び出しを見て、それが何をするかを伝えるのに十分な長さのパラメーターを覚えておく必要があるということです。そのため、メソッドを確認してからメソッドの呼び出しに切り替えて、どのパラメーターが何を実行するかを覚えていると、多すぎます。
私にとっては約5に相当しますが、それほど明るくはありません。走行距離は異なる場合があります。
パラメータを保持するプロパティを持つオブジェクトを作成し、設定した制限を超えた場合にそれを渡すことができます。 Martin Fowlerのリファクタリングの本とメソッド呼び出しを簡単にする方法の章。
作業中の環境に大きく依存します。javascriptを例にとってみましょう。 JavaScriptでパラメーターを渡す最良の方法は、キー/値のペアを持つオブジェクトを使用することです。実際には、パラメーターは1つしかありません。他のシステムでは、スイートスポットは3または4になります。
最終的には、すべて個人的な趣味になります。
3は問題ありませんが、4はガイドラインとして多すぎます。 3つ以上のパラメーターを使用すると、必然的に複数のタスクを実行することになります。複数のタスクを別々のメソッドに分割する必要があります。
ただし、私が取り組んだ最新のプロジェクトを見ると、例外がたくさんあり、ほとんどの場合、3つのパラメーターを取得するのは難しいでしょう。
1つのルーチンに7〜10個のパラメーターがある場合、それらを新しいクラスにバンドルすることを検討します そのクラスがゲッターとセッターを持つフィールドの束にすぎない場合-新しいクラスシャッフル値以外の何かを行う必要があります。それ以外の場合は、長いパラメーターリストに我慢します。
平均して、人々は一度に7つ+/- 2つの物を頭の中に入れることができるということは知られている事実です。私はその原理をパラメーターとともに使用するのが好きです。プログラマーはすべて平均以上の知的な人だと仮定すると、10以上のすべてが多すぎると思います。
ところで、パラメータが何らかの形で似ている場合、構造体やクラスではなくベクトルまたはリストに入れます。
関数が呼び出される頻度に基づいて答えを決めます。
一度しか呼び出されないinit関数の場合、気にする人は10 parms以上かかります。
フレームごとの束と呼ばれる場合、構造を作成し、ポインタを渡す傾向があります(構造体を毎回再構築していないと仮定すると) p>
Amazonの名声のJeff Bezosによると、ピザ2枚を与えることしかできない: