web-dev-qa-db-ja.com

上司から小さな関数の作成をやめて、すべて同じループでやるように言われる

Robert C. Martinの Clean Code という本を読んだことがあります。この本では、小さな関数を書いたり、名前を慎重に選択したりするなど、コードをクリーンアップする多くの方法を見てきました。これまで読んだクリーンなコードについて、これは最も興味深い本のようです。しかし、今日、上司は、この本を読んだ後にコードを書く方法が気に入らなかった。

彼の議論は

  • 小さな関数を書くのは面倒です。なぜなら、コードが何をしているかを確認するために各小さな関数に移動する必要があるからです。
  • メインループが300行を超える場合でも、すべてをメインの大きなループに入れると、読みやすくなります。
  • コードを複製する必要がある場合にのみ、小さな関数を記述します。
  • コメントの名前で関数を記述しないでください。上のコメントを含む複雑なコード行(3〜4行)を配置してください。同様に、失敗したコードを直接変更できます

これは私が読んだすべてに反対です。通常、どのようにコードを記述しますか? 1つの大きなループ、小さな関数はありませんか?

私が使用する言語は主にJavaScriptです。明確に名前が付けられた小さな関数をすべて削除し、すべてを大きなループに入れているので、今は本当に読むのが困難です。しかし、上司はこのようにそれを好きです。

一例は:

// The way I would write it
if (isApplicationInProduction(headers)) {
  phoneNumber = headers.resourceId;
} else {
  phoneNumber = DEV_PHONE_NUMBER;
}

function isApplicationInProduction(headers) {
   return _.has(headers, 'resourceId');
}

// The way he would write it
// Take the right resourceId if application is in production
phoneNumber = headers.resourceId ? headers.resourceId : DEV_PHONE_NUMBER;

たとえば、私が読んだ本では、コメントはクリーンなコードの作成に失敗したと見なされます。これは、小さな関数を書くと陳腐化し、更新されないコメントにつながることが多いためです(コメントではなくコードを変更します)。しかし、私がやっていることはコメントを削除して、コメントの名前で関数を書くことです。

さて、私はいくつかのアドバイスをお願いします、どの方法/実践がクリーンなコードを書く方が良いですか?

209

最初にコード例を取り上げます。あなたが好む:

if (isApplicationInProduction(headers)) {
  phoneNumber = headers.resourceId;
} else {
  phoneNumber = DEV_PHONE_NUMBER;
}

function isApplicationInProduction(headers) {
   return _.has(headers, 'resourceId');
}

そしてあなたの上司はそれを次のように書くでしょう:

// Take the right resourceId if application is in production
phoneNumber = headers.resourceId ? headers.resourceId : DEV_PHONE_NUMBER;

私の見解では、どちらにも問題があります。あなたのコードを読んでいると、私はすぐに「ifを3項式で置き換えることができる」と考えました。それから私はあなたの上司のコードを読み、「なぜ彼はあなたの関数をコメントに置き換えたのですか?」と思いました。

最適なコードは2つの間にあることをお勧めします。

phoneNumber = isApplicationInProduction(headers) ? headers.resourceId : DEV_PHONE_NUMBER;

function isApplicationInProduction(headers) {
   return _.has(headers, 'resourceId');
}

これにより、両方の長所が得られます。簡略化されたテスト式とコメントがテスト可能なコードに置き換えられます。

しかし、コード設計に関する上司の見解について:

小さな関数を書くのは面倒です。なぜなら、コードが何をしているかを確認するために、それぞれの小さな関数に移動する必要があるからです。

関数の名前が適切である場合、これは当てはまりません。 isApplicationInProductionは自明であり、コードを調べて何が行われるかを確認する必要はありません。実際には反対のことが当てはまります。コードを調べると、関数名よりも意図が明らかになっていません(上司がコメントに頼らなければならないのはそのためです)。

メインループが300行を超える場合でも、すべてをメインの大きなループに入れると、読みやすくなります

スキャンする方が速いかもしれませんが、コードを本当に「読み取る」には、頭の中で効果的に実行できる必要があります。これは小さな関数では簡単で、数百行の長さのメソッドでは本当に難しいです。

コードを複製する必要がある場合は、小さな関数のみを記述してください

同意しません。コード例が示すように、小さくて名前の付いた関数はコードの読みやすさを向上させます。たとえば、「方法」に関心がなく、機能の「何」にのみ関心がある場合はいつでも使用できます。

コメントの名前で関数を記述しないでください。上のコメントを含む複雑なコード行(3〜4行)を記述してください。このように、失敗したコードを直接変更できます

これが本当に深刻であると仮定して、私はこれの背後にある理由を本当に理解することができません。それは、私がパロディで The Expert Beginner Twitterアカウントによって書かれると私が期待するようなものです。コメントには根本的な欠陥があります。コメントはコンパイル/解釈されないため、ユニットテストを実行できません。コードが変更されてコメントがそのままになり、どちらが正しいかわからなくなります。

自己文書化するコードを書くのは難しく、補足的なドキュメント(コメントの形式であっても)が必要になる場合があります。しかし、コメントがコーディングの失敗であるという「ボブおじさん」の見解は、あまりにも頻繁に当てはまる。

上司にClean Codeブックを読んでもらい、コードを読みにくくするだけで彼を満足させようとします。しかし結局のところ、彼を変えるように説得することができない場合は、列に並ぶか、より適切にコーディングできる新しいボスを見つける必要があります。

214
David Arno

その他の問題があります

どちらも基本的にデバッグテストケースでコードを膨張させるため、どちらのコードも適切ではありません。何らかの理由でより多くのものをテストしたい場合はどうでしょうか?

phoneNumber = DEV_PHONE_NUMBER_WHICH_CAUSED_PROBLEMS_FOR_CUSTOMERS;

または

phoneNumber = DEV_PHONE_NUMBER_FROM_OTHER_COUNTRY;

さらにブランチを追加しますか?

重要な問題は、基本的にコードの一部を複製しているため、実際のコードを実際にテストしていないことです。デバッグコードを記述してデバッグコードをテストしますが、製品コードはテストしません。これは、並列コードベースを部分的に作成するようなものです。

悪いコードをより賢く書く方法について上司と議論しています。代わりに、コード自体の固有の問題を修正する必要があります。

依存性注入

コードは次のようになります。

phoneNumber = headers.resourceId;

ここのロジックには分岐がないため、ここには分岐はありません。プログラムは、ヘッダーから電話番号をプルする必要があります。限目。

DEV_PHONE_NUMBER_FROM_OTHER_COUNTRY結果として、headers.resourceId。これを行う1つの方法は、テストケースに別のheadersオブジェクトを挿入することです(これが適切なコードでない場合、申し訳ありませんが、私のJavaScriptスキルは少し錆びています)。

function foo(headers){
    phoneNumber = headers.resourceId;
}

// Creating the test case
foo({resourceId: DEV_PHONE_NUMBER_FROM_OTHER_COUNTRY});

headersがサーバーから受け取る応答の一部であると仮定します。理想的には、テスト目的でさまざまな種類のheadersを配信するテストサーバー全体が必要です。 この方法では、実際の製品コードを現状のままテストします。製品コードのように機能するかどうかわからない、半分が重複するコードはテストしません。

222
null

これに対する「正しい」または「間違った」答えはありません。ただし、ソフトウェアシステムの設計と開発の36年の専門的な経験に基づいて私の意見を述べます...

  1. 「自己文書化コード」などはありません。どうして?それは完全に主観的に主張しているからです。
  2. コメントは失敗ではありません。何is失敗とは、まったく理解できないコードwithoutコメントです。
  3. 1つのコードブロック内の300行の連続したコードは、メンテナンスの悪夢であり、エラーが発生しやすい傾向があります。このようなブロックは、設計と計画が悪いことを強く示しています。

あなたが提供した例に直接話して... isApplicationInProduction()を独自のルーチンに配置するのは、賢いことです。現在、このテストは単に「ヘッダー」のチェックであり、3項(_?:_)演算子で処理できます。明日、テストはもっと複雑になるかもしれません。さらに、「headers.resourceId」とアプリケーションの「稼働中のステータス」との明確な関係はありません。そのようなステータスのテストneedsは、基礎となるデータから切り離されると主張します。サブルーチンはこれを行い、三項は行いません。さらに、役立つコメントはwhy resourceIdであり、「本番環境」のテストです。

「明確に名前が付けられた小さな関数」を使いすぎないように注意してください。ルーチンは、「単なるコード」ではなく、アイデアをカプセル化する必要があります。 phoneNumber = getPhoneNumber(headers)のamonの提案をサポートし、getPhoneNumber()isApplicationInProduction()を使用して「プロダクションステータス」テストを実行する必要があることを追加します

59
LiamF

「エンティティは、必要以上に増加してはなりません。」

- オッカムのかみそり

コードはできるだけ単純でなければなりません。バグは複雑なものの間に隠れることを好みます。バグを見つけるのは難しいからです。では、何がコードをシンプルにするのでしょうか?

小さな単位(ファイル、関数、クラス)は良い考えです。小さなユニットは、一度に理解する必要のあるものが少ないため、理解しやすいです。通常の人間は一度に7つの概念しか操作できません。ただし、サイズは、コード行で測定されるだけではありません。コードを「ゴルフ」して(短い変数名を選択し、「賢い」ショートカットを使用し、できるだけ多くのコードを1行にまとめて)、できるだけ少ないコードを書くことができますが、最終結果は単純ではありません。このようなコードを理解しようとすることは、読むよりもリバースエンジニアリングに似ています。

関数を短くする1つの方法は、さまざまなヘルパー関数を抽出することです。これは、自己完結型の複雑さを抽出する場合に適しています。独立して、その複雑さの部分は、無関係の問題に組み込まれている場合よりも、管理(およびテスト)がはるかに簡単です。

しかし、すべての関数呼び出しには認知的オーバーヘッドがあります:コードを理解する必要はありませんwithin私の現在のコードは、outsideのコードとどのように相互作用するかも理解する必要があります。 抽出した関数は、抽出した関数よりも関数が複雑になると言って差し支えないと思います。 「小さな関数が上司が意味するのはそれです。コードが何をしているのかを確認するために、各小さな関数に移動する必要があるためです。

長い退屈な関数は、数百行の長さであっても、理解するのが非常に簡単な場合があります。これは、初期化および構成コードで発生する傾向があります。ドラッグアンドドロップエディターなしで手動でGUIを作成する場合。合理的に抽出できる自己完結型の複雑さはありません。しかし、書式設定が読みやすく、コメントがいくつかある場合、何が起こっているかを追跡することは実際には難しくありません。

他にも多くの複雑なメトリックがあります:スコープ内の変数の変数の数は可能な限り少なくする必要があります。これは、変数をavoidする必要があるという意味ではありません。これは、各変数を、それが必要とされる可能な限り小さいスコープに制限する必要があることを意味します。変数に含まれる値を変更しない場合も、変数はより単純になります。

非常に重要な指標は、循環的複雑度(McCabe複雑度)です。コードの一部を介して独立したパスの数を測定します。この数は、各条件付きで指数関数的に増加します。各条件付きループまたはループは、パスの数を2倍にします。 10ポイントを超えるスコアは複雑すぎることを示唆する証拠があります。つまり、スコアが5の非常に長い関数は、スコアが25の非常に短くて密な関数よりも優れている可能性があります。制御フローを個別の関数に抽出することで、複雑さを軽減できます。

あなたの条件は完全に抽出できる複雑さの一部の例です:

function bigFatFunction(...) {
  ...
  phoneNumber = getPhoneNumber(headers);
  ...
}

...

function getPhoneNumber(headers) {
  return headers.resourceId ? headers.resourceId : DEV_PHONE_NUMBER;
}

これは、まだ非常に便利です。 この条件式はそれほど条件的ではないため、複雑さが大幅に減少するかどうかはわかりません。本番環境では、常に同じパスを使用します。


複雑さが消えることはありません。 多くの小さなものは、いくつかの大きなものよりも単純ですか?それは状況に大きく依存します。通常、ちょうどいい感じの組み合わせがいくつかあります。さまざまな複雑さの要素間の妥協点を見つけるには、直感と経験、そして少しの運が必要です。

非常に小さな関数と非常に単純な関数の書き方を知ることは、代替案を知らなければ選択を行うことができないため、有用なスキルです。 ルールまたはベストプラクティスを盲目的に適用すると、それらが現在の状況にどのように適用されるかを考えずに、せいぜい平均的な結果につながります。最悪のカーゴカルトプログラミング

それは私があなたの上司に反対するところです。彼の議論は無効ではありませんが、クリーンコードブックも間違っていません。おそらく上司のガイドラインに従う方が良いでしょうが、これらの問題について考え、より良い方法を見つけようとしているという事実は非常に有望です。経験を積むにつれて、コードの適切な因数分解を見つけやすくなります。

(注:この回答は、Jimmyによる Reasonable Codeブログ投稿The Whiteboardからの考えに一部基づいていますHoffa は、コードを単純にする理由についての概要を提供します。)

47
amon

ロバートマーティンのプログラミングスタイルは二極化しています。あなたは多くのプログラマーを経験している人でさえ、なぜ「それだけ」を分割することが多すぎるのか、そして関数を少し大きく保つことが「より良い方法」なのか多くの言い訳を見つけます。ただし、これらの「議論」のほとんどは、多くの場合、古い習慣を変更したり、何か新しいことを学びたがらないことを表しています。

聞いてはいけません!

コードの一部を表現力豊かな名前の別の関数にリファクタリングすることでコメントを保存できる場合はいつでもそれを実行してください-コードを改善する可能性が最も高いでしょう。ボブ・マーティンが彼のクリーンなコードブックでそれを行うほど遠くまでは行きませんが、私が過去に見た保守の問題を引き起こしたコードの大部分は、小さすぎるものではなく、大きすぎる関数を含んでいました。したがって、自己記述的な名前で小さな関数を記述しようとすることは、あなたが試すべきことです。

自動リファクタリングツールにより、メソッドの抽出が簡単、シンプル、安全になります。また、300行を超える関数の記述を推奨する真面目な人は避けてください。そのような人は、コーディングの仕方を教える資格がありません。

27
Doc Brown

あなたの場合:電話番号が必要です。電話番号をどのように取得するかが明らかな場合は、明白なコードを記述します。または、電話番号を取得する方法が明確でない場合は、そのためのメソッドを記述します。

あなたの場合、電話番号を取得する方法は明らかではないため、そのためのメソッドを記述します。実装は明確ではありませんが、それを別のメソッドに配置するのはそのため、1回だけ処理する必要があります。実装が明確でないため、コメントは役に立ちます。

「isApplicationInProduction」メソッドはまったく無意味です。 getPhonenumberメソッドからそれを呼び出しても、実装が明確になるわけではなく、何が起こっているのかを理解するのが難しくなるだけです。

小さな関数を書かないでください。明確に定義された目的を持ち、その明確に定義された目的を満たす関数を記述します。

PS。私はその実装がまったく好きではありません。電話番号がないことは、それが開発バージョンであることを意味すると想定しています。したがって、電話番号が本番環境にない場合は、それを処理するだけでなく、ランダムな電話番号に置き換えてください。 10,000人の顧客がいて、17人に電話番号がなく、生産に問題があるとします。あなたが本番にいるのか開発にいるのかは、他のものから派生したものではなく、直接チェックする必要があります。

23
gnasher729

どちらの実装もそれほど優れていないという事実を無視しても、これは本質的に、少なくとも使い捨ての簡単な関数を抽象化するレベルでの好みの問題であることに注意します。

行数は、ほとんどの場合、有用なメトリックではありません。

300(または3000)行の完全に自明な純粋な順次コード(セットアップなど)はめったに問題になりません(ただし、自動生成されるか、データテーブルなどとしてはより良いかもしれません)。ガウスの消去法や行列の反転などで見られるような終了条件や数学は、簡単にたどることができません。

私にとっては、宣言するために必要なコードの量が実装を形成するコードの量よりもはるかに少ない場合を除いて、私は使い捨て関数を作成しませんでした(私がフォールトインジェクションを簡単に実行できるようにしたいという理由がない限り)。 1つの条件がこの法案に適合することはめったにありません。

今、私は小さなコアの組み込み世界から来ており、スタックの深さやコール/リターンのオーバーヘッドなども考慮する必要があります(ここでも、ここで提唱されているような小さな関数に反論しています)、これが私の設計にバイアスをかけている可能性があります決定ですが、コードレビューで元の関数を見つけた場合、古いスタイルのusenetフレームが返されます。

味はデザインが教えるのが難しく、実際には経験でしか得られないので、関数の長さに関するルールに減らすことができるかどうかはわかりません。また、循環的な複雑さでさえ、メトリックとしての制限があります(状況に応じて複雑になることがあります)。
これは、クリーンなコードがいくつかの良いものを議論しないと言っているわけではありません。そして、これらのことは考慮されるべきですが、ローカルのカスタムと既存のコードベースが何をするかについても重要視されるべきです。

この特定の例は、些細なことを選んでいるように見えますが、システムを簡単に理解してデバッグする機能の方がはるかに重要であるため、はるかに高いレベルのものにもっと関心があります。

16
Dan Mills

すべてを1つの大きなループに入れないでください。ただし、あまり頻繁に行わないでください。

function isApplicationInProduction(headers) {
   return _.has(headers, 'resourceId');
}

大きなループの問題は、多くの画面にまたがるときに全体的な構造を見るのが本当に難しいことです。したがって、大きなチャンク、理想的には単一の責任を持ち、再利用可能なチャンクを取り出そうとします。

上記の小さな関数の問題は、原子性とモジュール性は一般的に優れているものの、あまりにも遠すぎる可能性があることです。上記の関数を再利用しない場合は、コードの可読性と保守性が損なわれます。詳細にドリルダウンするには、詳細をインラインで読み取ることができる代わりに、関数にジャンプする必要があります。関数呼び出しは、詳細自体よりも少ないスペースをほとんど占有しません。

明らかに多すぎるを実行するメソッドと少なすぎるを実行するメソッドの間にはバランスが見られます。関数が複数の場所から呼び出される場合を除いて、上記のような小さな関数を分解することは絶対にありません。それでも、関数についてはそれほど重要ではない新しいロジックを導入するという用語は、それ自体が存在することをほとんど保証しません。

15
Brad Thomas

あなたが実際に欲しいのはこれです:

phoneNumber = headers.resourceId || DEV_PHONE_NUMBER

これは、それを読む人には一目瞭然です:利用可能な場合はphoneNumberresourceIdに設定し、利用できない場合はデフォルトでDEV_PHONE_NUMBERに設定します。

trulyが本番環境でのみその変数を設定したい場合、どこから実行しているのかを特定するために、他のより標準的なアプリ全体のユーティリティメソッド(パラメーターを必要としない)が必要です。その情報のヘッダーを読み取っても意味がありません。

率直に言ってください。あなたの環境(言語/フレームワーク/クラスの設計など)は、「クリーン」なコードにはあまり適していないようです。あなたは、実際にはあまり接近してはならない数行のコードで、あらゆる種類のことを混合しています。 resourceId==undefは、本番環境ではないこと、非本番システムでデフォルトの電話番号を使用していること、resourceIdが「ヘッダー」などに保存されていることを意味しますか?私はheadersがHTTPヘッダーであることを想定しているので、どの環境にいるのかについての決定をエンドユーザーに任せますか?

そのうちの1つを関数に分解しても、その根本的な問題についてはあまり役に立ちません。

検索するいくつかのキーワード:

  • デカップリング
  • 凝集
  • 依存性注入

コードの行数をゼロにして、コードの責任をシフトし、最新のフレームワーク(環境/プログラミング言語に存在する場合と存在しない場合がある)を使用することで、(他のコンテキストで)必要なことを実現できます。

あなたの説明(「メイン関数の300行のコード」)から、(メソッドではなく)Wordの「関数」でさえ、達成しようとしていることに意味がないと私に思わせます。その古い学校のプログラミング環境(つまり、構造がほとんどなく、意味のあるクラスがなく、MVCなどのクラスフレームワークパターンがない基本的な命令型プログラミング)では、anythingを実行しても意味がありません。基本的な変更がなければ、水溜めから抜け出すことはできません。少なくともあなたの上司は、コードの重複を避けるための関数を作成できるようです。これは良い第一歩です!

私はあなたが非常にうまく記述しているコードのタイプとプログラマーのタイプの両方を知っています。率直に言って、もしそれが同僚なら、私のアドバイスは違うでしょう。しかし、それはあなたの上司なので、これについて戦うのは無意味です。上司があなたを覆す可能性があるだけでなく、あなたが部分的に「あなたのこと」をし、上司(そしておそらく他の人々)が以前のように続ければ、コードの追加は確かに悪いコードにつながります。彼らのプログラミングスタイルに適応し(もちろんこの特定のコードベースで作業しているときのみ)、このコンテキストでそれを最大限に活用しようとすると、最終結果にとってより良いかもしれません。

14
AnoE

「クリーン」は、コードを書く際の1つの目標です。それだけではありません。別の価値のある目標はcolocalityです。非公式に言えば、共存性とは、コードを理解しようとする人々が、あなたが何をしているかを確認するためにあちこち飛び回る必要がないことを意味します。三項式の代わりに適切な名前の関数を使用するのは良いことのように思えるかもしれませんが、そのような関数の数とそれらがどこにあるかによっては、この慣習が迷惑になることがあります。あなたがその一線を越えたかどうかはわかりませんが、人々が不平を言っているなら、あなたは特にあなたの就職状況について発言をしているなら、あなたは耳を傾けるべきだと言う以外は、言えません。

13
user1172763

小さな関数を使用することは、一般的には良い習慣です。しかし、理想的には、関数の導入により、大きな論理チャンクを分離するか、コードを乾燥させることによってコード全体のサイズを削減する必要があると思います。両方を指定した例では、コードが長くなり、開発者が読み取る時間が長くなりますが、短い代替案では、_"resourceId"_値が本番環境でのみ存在することは説明されていません。そのような単純なものは、特にコードベースをまだ初めて使用している場合は、それを操作しようとすると忘れやすく混乱しやすくなります。

あなたが絶対的に三項を使うべきだとは言いませんが、私が一緒に働いた人の中には少し長いif () {...} else {...}を好む人もいますが、それは主に個人的な選択です。私は「1行で1つのことを行う」アプローチを好む傾向がありますが、基本的には、コードベースが通常使用するものは何でも使います。

三項を使用する場合、論理チェックによって行が長くなりすぎたり複雑になったりする場合は、適切な名前の変数を作成して値を保持することを検討してください。

_// NOTE "resourceId" not present in dev build, use test data
let isProduction = 'resourceId' in headers;
let phoneNumber = isProduction ? headers.resourceId : DEV_PHONE_NUMBER;
_

また、コードベースが300行の関数に向かって伸びている場合は、ある程度の細分割が必要になるとも言えます。しかし、少し広いストロークの使用をお勧めします。

6
nepeo

あなたが与えたコード例、あなたのボスIS CORRECT。その場合、単一の明確な行がより良いです。

一般に、複雑なロジックを小さい部分に分割することは、読みやすさ、コードのメンテナンス、およびサブクラスが異なる動作を(わずかであっても)持つ可能性のために優れています。

デメリットを無視しないでください:関数のオーバーヘッド、隠蔽(関数はコメントや関数名が意味することを実行しません)、複雑なスパゲッティロジック、デッド関数の可能性(一度にその目的のために作成されました)呼び出されなくなりました)。

5
Phil M

長い関数を支持する少なくとも2つの引数を考えることができます。

  • これは、各行の周りに多くのコンテキストがあることを意味します。これを形式化する方法:コードの制御フローグラフを描画します。関数の入口と関数の出口の間の頂点(〜=行)で、入力エッジのallがわかります。関数が長いほど、そのような頂点が多くなります。

  • 多くの小さな関数は、より大きくより複雑な呼び出しグラフがあることを意味します。ランダム関数のランダムな行を選び、「この行はどのコンテキストで実行されますか?」という質問に答えます。これは、呼び出しグラフが大きくて複雑になるほど難しくなります。これは、そのグラフでより多くの頂点を調べる必要があるためです。

長い関数に対する議論もあります—ユニットテストの可能性が思い浮かびます。どちらか一方を選択するときは、あなたの経験を使用してください。

注:私はあなたの上司が正しいと言っているのではなく、彼の上司が完全に価値がないわけではないということだけです。


私の考えでは、適切な最適化パラメーターは関数の長さではありません。私は、次の点を考えると、より有用であると思います。他はすべて等しいので、ビジネスロジックと実装の両方の高レベルの記述をコードから読み取ることができることが望ましいです。 (コードの関連ビットが見つかれば、低レベルの実装の詳細をいつでも読み取ることができます。)


David Arnoの回答へのコメント

小さな関数を書くのは面倒です。なぜなら、コードが何をしているかを確認するために、それぞれの小さな関数に移動する必要があるからです。

関数の名前が適切である場合、これは当てはまりません。 isApplicationInProductionは自明であり、コードを調べて何が行われるかを確認する必要はありません。実際には反対のことが当てはまります。コードを調べると、関数名よりも意図が明らかになっていません(上司がコメントに頼らなければならないのはそのためです)。

名前は戻り値手段を明確にしますが、コードの実行の効果については何も述べていません(=コードの内容行う)。名前(のみ)はintentに関する情報を伝達し、コードはbehaviorに関する情報を伝達します(そこから意図の一部が推測される場合があります)。

場合によっては一方が必要なこともあれば、もう一方が必要なこともあります。そのため、この観察は一方的に普遍的に有効な決定ルールを作成しません。

メインループが300行を超える場合でも、すべてをメインの大きなループに入れると、読みやすくなります

スキャンする方が速いかもしれませんが、コードを本当に「読み取る」には、頭の中で効果的に実行できる必要があります。これは小さな関数では簡単で、数百行の長さのメソッドでは本当に難しいです。

頭の中でそれを実行しなければならないことに同意します。 1つの大きな関数と多くの小さな関数に500行の機能がある場合、なぜこれが簡単になるのかはわかりません。

500行の極端に影響が大きい直線的なコードの極端なケースで、エフェクトAがエフェクトBの前または後に発生するかどうかを知りたいとします。大きな関数の場合は、Page Up/Downを使用して2行を特定し、比較します。行番号。多くの小さな関数の場合、コールツリーのどこで効果が発生するかを覚えておく必要があり、忘れてしまった場合は、このツリーの構造を再発見するために重要な時間を費やす必要があります。

サポート関数の呼び出しツリーをたどるとき、ビジネスロジックから実装の詳細にいつ移行したかを判断するという課題にも直面します。私は証拠なしで主張します*コールグラフが単純であるほど、この区別を容易にすることです。

(*)少なくとも私はそれについて正直です;-)

繰り返しますが、どちらのアプローチにも長所と短所があると思います。

コードを複製する必要がある場合は、小さな関数のみを記述してください

同意しません。コード例が示すように、小さくて名前の付いた関数はコードの読みやすさを向上させるため、[方法]に興味がなく、機能の[何]にのみ関心がある場合は常に使用する必要があります。

「どのように」または「何」に興味があるかどうかは、コードを読んでいる目的の関数です(たとえば、一般的なアイデアを得るか、バグを追跡するか)。プログラムを作成している間は、コードを読み取る目的は利用できません。また、さまざまな目的でコードを読み取る可能性があります。異なる決定は、異なる目的のために最適化されます。

とはいえ、これは私がおそらく最も同意しない上司の見解の一部です。

コメントの名前で関数を記述しないでください。上のコメントを含む複雑なコード行(3〜4行)を記述してください。このように、失敗したコードを直接変更できます

これが本当に深刻であると仮定して、私はこれの背後にある理由を本当に理解することができません。 [...]コメントには根本的な欠陥があります。コメントはコンパイル/解釈されないため、ユニットテストを実行できません。コードが変更されてコメントがそのままになり、どちらが正しいかわからなくなります。

コンパイラは名前が等しいかどうかを比較するだけで、MisleadingNameErrorを与えることはありません。また、複数の呼び出しサイトが特定の関数を名前で呼び出す可能性があるため、名前を変更するのが困難でエラーが発生しやすい場合があります。コメントにはこの問題はありません。ただし、これはいくぶん推測的なものです。これを本当に解決するには、おそらくプログラマが誤解を招くようなコメントと誤解を招くような名前を更新する可能性が高いかどうかについてのデータが必要になるでしょう。私にはそれがありません。

2
Jonas Kölker