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