関数の長さはプログラマの生産性に影響しますか?もしそうなら、生産性の損失を避けるための適切な最大行数は何ですか?
これは非常に意見の多いトピックであるため、いくつかのデータを使用してクレームをバックアップしてください。
関数の長さはプログラマの生産性に影響しますか?もしそうなら、生産性の損失を避けるための適切な最大行数は何ですか?
これは非常に意見の多いトピックであるため、いくつかのデータを使用してクレームをバックアップしてください。
回答:
1970年にこのクレイジーなラケットに乗り出してから、実際には複数の印刷ページ(約60行)が必要なモジュールを1つだけ見ました。もっと長いモジュールを見てきました。
さらに言えば、より長いモジュールを作成しましたが、通常は大きなスイッチステートメントとして作成された大規模な有限状態マシンでした。
問題の一部は、最近のプログラマーが物事をモジュール化することを教えられていないことのようです。
垂直方向のスペースの無駄を最大限にするコーディング標準も問題の一部であるように見えます。(私はまだGerald Weinbergの「コンピュータプログラミングの心理学」を読んだソフトウェアマネージャーに会ったことがありません。プログラマーはスクロールするか、ページをめくる必要があり、理解度が大幅に低下します:覚えて、抽象化する必要があります。)
FORTHによる十分に文書化されたプログラマの生産性向上の多くは、ソースコードのFORTH "ブロック"システムによるものであると確信しています。無限に因数分解できますが、どのような状況でも 17行のルーチンを書くことはできません。
使用する言語に依存しますが、一般的に(そして私の個人的な好みのために):
それ以上の場合は、後で戻ってやり直す必要があります。
しかし、現実的には、何かを配達する必要があるときに必要なサイズであり、そのように吐き出す瞬間にもっと理にかなっているので、誰かが出荷前にレビューするのがさらに簡単になります。(ただし、後でそれに戻ります)。
(最近、私のチームはコードベースでプログラムを実行しました。メソッドが197個、メソッドが3個だけのクラスが見つかりましたが、そのうちの1つは600行でした。かわいいゲーム:2つの悪の悪いところは何ですか?)
さて、もっと禅の答えを...概して、1人か2人の偉大な男性を引用するのは良い習慣(TM)と考えられているので、ここに行きます:
すべてをできるだけシンプルにする必要がありますが、シンプルにする必要はありません。- A.アインシュタイン
最終的に完成するのは、追加するものがなくなったときではなく、奪うものがなくなったときです。- A.・ド・サン=テグジュペリ
これに対する補遺として、関数には、その意図を説明する明確な名前を付ける必要があります。コメントに関しては、通常、関数内ではコメントしません。
(説明が必要な)各関数の上部にあるコメントブロックで十分です。関数が小さく、関数名が十分に明確な場合は、達成したいこととその理由を言うだけで十分です。インラインコメントは、一部の言語のフィールド、または意図が不明な場合に25〜35行の規則に違反する関数のブロックスタートでのみ使用します。例外的な状況が発生したときに、コード内でブロックコメントを使用します(たとえば、必要のない、または何もしたくないcatchブロックには、理由を示すコメントが必要です)。
詳細については、読んでください私の答えのスタイルとコメントコードの勧告を
tt
これらを生成するために使用しますが、時々、とにかく興味のあることは何もしないので、実際の問題ではない長いお尻の機能(または長いお尻の機能)で立ち往生しています。
Map(x => x.Property1); Map(x => x.Property2); Map(x => x.Property3);
すべて同じであることが明らかです。(これは単なる例であり、この種の関数は時々ポップアップすることに注意してください)
私の意見では、すべての機能はできるだけ小さくする必要があります。各関数は1つだけを実行し、適切に実行する必要があります。それは実際には最大長の質問に答えるわけではありませんが、関数の長さについての私の気持ちです。
ボブおじさんの言葉を使うには、「もう抽出できないまで抽出してください。ドロップするまで抽出してください。」
建物の最大高さはどのくらいですか?ビルドがどこにあるか、または希望する高さに依存します。
あなたは異なる都市から来る異なる人々から異なる答えを得るかもしれません。
一部のスクリプト関数とカーネル割り込みハンドラーは非常に長いです。
トレードオフがあると思います。短いメソッドが多数ある場合、1つの長いメソッドよりもデバッグするのが難しいことがよくあります。1回のメソッド呼び出しをトレースするためにエディターを20回または30回ジャンプする必要がある場合、すべてを頭の中に保持するのは困難です。一方、明確に書かれた明確な方法が1つあれば、たとえ100行であっても、頭の中に留めておいた方が簡単です。
本当の問題は、アイテムを異なる方法にする必要がある理由であり、上記の答えはコードの再利用です。コードを再利用していない(または知らない)場合は、1つの巨大な簡単な方法でそのままにしておくことは理にかなっています。再利用する必要がある場合は、再利用が必要な部分を分割します。より小さなメソッドに使用します。
実際には、優れたメソッド設計の一部は、機能的に凝集したメソッドを作成することです(本質的に1つのことを行います)。メソッドの長さは関係ありません。関数が1つの明確に定義された処理を実行し、1,000行である場合、それは良い方法です。関数が3つまたは4つのことを行い、15行しかない場合、それは悪い方法です...
関数全体を一度に見ることができれば、自分がやっていることを追跡するのが簡単になります。だから私は関数を書くことを好む方法です:
それより長い関数を書くことはめったにありません。それらのほとんどは、巨大なC / C ++ switchステートメントです。
問題は、関数が何をする必要があるかです。そして通常、「1」のことをするために100行が必要になることはまれです。繰り返しますが、それはあなたがコードを見ているレベルに依存します:パスワードをハッシュすることは一つのことですか?または、パスワードをハッシュして保存することは一つのことですか?
1つの機能としてパスワードの保存から始めましょう。ハッシュが異なると感じ、コードをリファクタリングするとき。私は決して熟練したプログラマーではありませんが、私見では、関数の全体的なアイデアは小さく始まります。など
1000行を超えるSQL ストアドプロシージャを見てきました。ストアドプロシージャの行数も50未満ですか?わかりませんが、コードを読むのは大変です。上下にスクロールし続ける必要があるだけでなく、数行のコードに「this does validation1」、「this updates in the database」などの名前を付ける必要があります。これはプログラマが行うべき作業です。
以下からの循環的複雑度(ウィキペディア):
ソースコードのセクションの循環的な複雑さは、ソースコードを通る直線的に独立したパスの数です。
1つの方法でその数を10未満に抑えることをお勧めします。10に達したら、リファクタリングの時間です。
コードを評価し、循環的複雑度を示すことができるツールがあります。
これらのツールをビルドパイプラインに統合するよう努力する必要があります。
文字通りメソッドのサイズを追いかけるのではなく、その複雑さと責任に注目してください。複数の責任がある場合は、リファクタリングすることをお勧めします。その循環的な複雑さが増すと、おそらくリファクタリングの時間です。
同様のフィードバックを提供するツールは他にもあると確信していますが、まだこれを調べる機会はありませんでした。
通常、メソッド/機能を1680x1050モニターの画面に適合するものに維持しようとしています。収まらない場合は、ヘルパーメソッド/関数を使用してタスクを分割します。
画面と紙の両方で読みやすくします。
一部の関数は本質的に複雑なアルゴリズムを実装しており、それらを短くしようとすると、新しい短い関数間の相互作用が非常に複雑になるため、最終的な結果は単純さを低下させないため、私は何にも強い制限を設けません。また、関数は「1つのこと」だけを行うべきだという考えは良いガイドではないと思います。なぜなら、高レベルの抽象化における「1つのこと」は、より低いレベルでは「多くのこと」になるからです。
私にとって、関数の長さがDRYのわずかな違反を引き起こしている場合、関数は間違いなく長すぎます。関数の一部を新しい関数またはクラスに抽出すると、これを解決できます。これが当てはまらない場合、関数は長すぎるかもしれませんが、将来の予測可能な変更に直面する可能性が高い方法でコードをよりモジュール化する関数またはクラスを簡単に抽出できます。
正しく最適化できるほど短い
メソッドは、1つだけを実行できるように短くする必要があります。この理由は簡単です。コードを適切に最適化できます。
JavaやC#のようなJIT化言語では、JITコンパイラーがコードを迅速に生成できるように、メソッドが単純であることが重要です。より長く、より複雑なメソッドは当然、より多くのJIT時間を必要とします。また、JITコンパイラーはほんの一握りの最適化のみを提供し、最も単純なメソッドのみがこの恩恵を受けます。この事実は、ビルワーグナーのEffective C#でも言及されていました。
CやC ++などの低レベル言語では、短いメソッド(数十行程度)を持つことも重要です。これにより、ローカル変数をレジスタではなくRAMに格納する必要性を最小限に抑えることができます。(別名「流出の登録」)。ただし、この管理されていない場合、各関数呼び出しの相対コストはかなり高くなる可能性があることに注意してください。
また、RubyやPythonなどの動的言語でも、短いメソッドを使用すると、コンパイラーの最適化にも役立ちます。動的言語では、機能が「動的」であるほど、最適化が難しくなります。たとえば、Xを受け取り、Int、Float、またはStringを返す可能性のある長いメソッドは、それぞれが単一の型のみを返す3つの個別のメソッドよりもはるかに遅いパフォーマンスを発揮します。これは、コンパイラが関数が返す型を正確に知っている場合、関数呼び出しサイトも最適化できるためです。(たとえば、型変換をチェックしない。)
私の一般的なルールは、関数が画面に収まるようにすることです。これに違反する傾向があることがわかったのは、3つのケースのみです。
1)関数をディスパッチします。昔はこれらは一般的でしたが、最近ではそれらのほとんどがオブジェクトの継承に置き換えられています。ただし、オブジェクトはプログラム内でのみ機能するため、他の場所から到着するデータを処理するときに、時折ディスパッチ機能が表示されます。
2)目標を達成するために一連のステップを実行する機能、およびステップに適切な細分化が欠けている場合。単純に他の関数の長いリストを順番に呼び出す関数になります。
3)#2と似ていますが、個々のステップが非常に小さいため、個別に呼び出されるのではなく、単にインライン化されます。
たぶん、関数の長さはそれほど良いメトリックではありません。メソッドでも循環的複雑度を使用しようとします。また、クラスおよびメソッド上の循環的複雑度はXより低くなければならないという将来のソース管理チェックイン規則の1つです。
メソッドの場合、Xは30に設定されますが、これは非常に厳密です。