ピアニストのようにコードを書くコードの芸術 | JD Cloud Technology Team

序文

システムの品質を評価するにはどうすればよいですか? どのようなシステムやソフトウェアが高品質と言えるのでしょうか?それは 3 つの観点から見ることができ、1 つは技術の選択、分散システムにおけるデータの一貫性の考慮などのアーキテクチャ設計、2 つ目はプロジェクト管理で、アジャイル開発であってもウォーターフォール開発であっても、技術的負債をクリーンアップする必要があります。 、コードのリファクタリングなど、最後にコードの品質と切り離すことはできません。コードの品質はシステムの保守性とスケーラビリティに直接影響します。車のようなもので、内装は高級感があり、見た目も美しいのですが、足回りは貧弱でパワーも弱く、決して良い車とは言えません。この記事では、プログラマーとしてクリーンで高品質なコードを書く方法を主観的および客観的な観点から説明します。

主観的な視点

エンジニア魂

JingME アバターをクリックすると、社内の役職が「xx コード ライター」ではなく「xx 開発エンジニア」であることがわかります。この部門は修士部門 (マスター) として理解できます。マスター プログラマーは、システムを文章を書くのではなく、物語として捉えます。それをプログラムとして開発者として、自分が書いたコードには責任を持つ必要があります。クラスに @author という名前を付けると、達成感を感じるはずです。将来のある日、おそらく 1 年後、2 年後、さらには 5 年後、その他のこのコードを読んだ後、あなたは「一体何を書いたんだ?」と文句を言うのではなく、心から「かっこいい」ため息をつくでしょう。Doug Lea によって書かれた同時実行パッケージは、何年も経った今でも有名です。

コードの可読性の向上

最初に到達すべきことは、コードは人が読めるように書かれているということです。コードはマシンが実行するための基本的なものにすぎません。優れたコードには多くのコメントは必要ありません。コード自体はコメントです可読性とは、他の開発者がコードの意図と機能をすぐに理解できる能力を指します。簡潔なコードは、理解、テスト、保守が容易です。次に、重複を避けます。コードの重複はソフトウェアにおける諸悪の根源であり、重複を排除するために多くの原則と設計パターンが作成されました。

客観的な視点

次に客観的な視点から、日々のコードレビューでよくある問題の例をあげて参考にしていきます。

トライ/キャッチ使用

1. try/catch ブロックはできるだけ小さくし、予測不可能な状況の場合にのみ try/catch を実行する必要があり、試行する前に予見可能な例外をチェックする必要があります。

予期せぬ状況: リモート呼び出し、IO 読み取りおよび書き込み、サードパーティ API など。予測可能な状況: パラメーターが空である、リストに要素がないなど。



小さな Try/Catch ブロックの利点は何ですか?

  • コードが整然としており、他のコードのインデントを減らすことができます。
  • 特定のプロンプト、UMP アラーム、ログ出力、または特定の例外に対するエラー コードの戻りを提供できる絶妙な例外制御

2. 本当にメソッド全体を tryCatch する必要がある場合は、ビジネス ロジックの処理と例外処理を分離する新しいメソッドを作成することをお勧めします。

例えば:



try/catch コード ブロックには多くのコンテンツがあり、コード制御能力が十分ではないことを示しています 。try がある場合は、try が最初の行にあり、他のコンテンツがあってはいけないことを確認してください。キャッチ後/最後に。

メソッドの長さ

メソッド行はできるだけ短くする必要があり、短ければ短いほど良いです。これにより、読者はクリックするとすぐにこのメソッドが何をするのかを理解できるようになります。例えば:



これは価格をクエリするためのメソッドです。このメソッドのスクリーンショットは長すぎて 1 ページに収まりません。このメソッドが何を行うのかを知りたい場合、メソッド本体のコメントだけでは簡単に理解することはできません。

簡単な処理をパッケージ化するだけで、コメントを一行書く必要もなく分かりやすい方法になります。

簡単なリファクタリング後:







上記のコードから判断すると、最初の段落では価格クエリ コードが 2 つの部分に分かれており、最初にクエリの承認ステータスを解析し、次に承認ステータスに応じて個別に価格をクエリします。2 番目の段落では、価格クエリを 2 つの部分に分割しています。1 つは承認中の価格をクエリするもので、もう 1 つは承認後の価格をクエリするもので、最後に 2 つが結合されて返されます。3 番目の段落は、承認時に価格をクエリするためのコードです。明らかに、承認中に価格をクエリするには、パーセンテージ モードと最低価格モードの 2 つのモードがあります。クエリ結果を照会した後、その組み合わせが返され、製品のチャネル価格が取得されます。 。

比較すると、元のコード ブロックが長すぎてネストが深く (for ループで if をネストしてから if をネストする)、その結果として可読性が低下していることがわかります。特定のコードを変更したい場合は、慎重に考える必要があります。しかし、分割後は他のメソッドへの影響を気にすることなく、目的のコードブロックを素早く見つけることができ、また単体テストを行うことでテストケースを書きやすくなります。

メソッド内で入力パラメータのデータを変更することは禁止されています

システム概念の観点から、システムは一般に 2 つのカテゴリに分類できます。1 つは入力が与えられ、出力が得られるタイプです。このタイプは応答型と呼ばれます。もう 1 つは入力は与えられますが、出力はありません。このタイプは、必須の。メソッドについても同様に、リアクティブ メソッドは入力パラメータを与え、出力パラメータを取得します。一般的なメソッドにはモデル変換、データ クエリなどが含まれますが、命令型メソッドにはメッセージの送信やスレッドの実行が含まれます。

しかし、どのように変化しても、入力データは変更されないという共通点があります。

1. 予期しない副作用の原因: メソッドが渡されたパラメーターを変更し、呼び出し元が元のパラメーター値に依存している場合、呼び出し元は知らないうちに影響を受ける可能性があります。これにより、プログラムの動作が予測不能になり、コードが複雑になり、エラーが発生する可能性が高まります。
2. データの一貫性を破壊する: 複数のメソッドが同じオブジェクトをパラメータとして共有し、これらのメソッドがすべてオブジェクトを変更できる場合、これらのメソッドが相互に干渉し、データの状態が混沌として予測不能になる可能性があります。
3. コードのメンテナンスが難しくなる可能性があります。メソッドが渡されたパラメータを変更する場合、メソッドを呼び出すコードはこの変更動作に依存する必要がある場合があります。これにより、コードの結合が増加し、理解、保守、再利用がより困難になります。メソッドを変更する必要がある場合、メソッドが呼び出されるすべての場所を同時に変更する必要がある場合があり、コードのメンテナンスコストが増加します。

例えば:



ここでは 3 つの set メソッドが呼び出されていますが、値がどのオブジェクトに設定されているかを一目でわかる人はいないでしょうか。値を割り当てる場所を知るには、1 つずつクリックして特定の実装を表示する必要があります。

本当にパラメータの値を変更する必要がある場合は、新しいオブジェクトを作成して変更した結果を保存し、呼び出し元に返すことができます。これにより、コードが明確になり、保守しやすく、テストしやすくなり、意図しない副作用やデータの一貫性の問題が軽減されます。

メソッドパラメータ

メソッドのパラメータは少ないほど良いです。理想的なパラメータはパラメータがなく、その後に 1 つのパラメータが続く状態です。3 つ以上のパラメータはできる限り避けるべきです。

上記のコードを続けると、最後の行は「Set Channel Type」です。set メソッドには 2 つの入力パラメータがあります。次のように、その割り当てオブジェクトを抽出して 1 つの入力パラメータを減らすことができます。



メソッドの処理ロジックが特定のローカル変数オブジェクトのデータのみに依存する場合、このメソッドをオブジェクト内に配置して、メソッド パラメータをさらに削減し、コードの再利用性を向上させることができます。次に例を示します。



この観点から見ると、2 つのパラメータ入力メソッドは、元のコードと比較して、パラメータのないメソッドに減らすことに成功しています。元のコードのメソッドの範囲はクラス内に限定されており、最後のパラメータは、 free メソッドはターゲットを取得するだけで済みます。Object インスタンスはどこからでも呼び出すことができるため、重複したコードを記述する必要性が軽減されます。

インデントを減らす

コード内のインデントが多すぎると、明らかに読み取りに影響します。可能であれば、コード内のインデントとレベルをできる限り減らす必要があります。優れたコードは、階段を上るのではなく、きちんとしたエレガントなレイアウトで新聞のように読める必要があります。

次のコードを見てみましょう。



コードには if のネストが 2 レベルありますが、まず、最初のレベルの if のネストを簡略化し、単純に if の判定条件を逆にすると、次のコードが得られます。



これにより、インデントのレベルが 1 つ減り、変数とパラメーターが微調整されます。



こうしてみると、以前のコードと比べて可読性は向上しているでしょうか?

単一責任

オブジェクト指向プログラミングにおける「単一責任原則」(SRP) は、クラスまたはモジュールが変更する理由は 1 つだけであるべきである、つまり、クラスまたはモジュールが主な責任または目標を 1 つだけ持つべきであることを意味します。機能と責任を改良することで、コードの保守性、再利用性、拡張性を向上させることができます。

以下のコードを見てください。



宣言の観点から見ると、このメソッドはタスク データを保存するために使用されますが、実際には、入力パラメーター内の一部のデータが空でない場合 (実際には、このメソッドの実装を見ると、一目で特定のデータを確認するのは困難です)メソッド)、更新操作を実行します。この書き方は読者を混乱させるだけでなく、コードのスケーラビリティも低下させます。

Q: AtomicInteger の CompareAndSet メソッドは単一責任に違反しますか? A: 単一責任の原則には違反しません。CompareAndSet メソッドは、AtomicInteger クラスによって提供されるアトミック操作です。現在の値が指定された期待値と等しいかどうかを比較するために使用されます。等しい場合は、現在の値が新しい値に設定されます。このメソッドの役割は、アトミックな比較と設定操作を実装して、マルチスレッド環境におけるスレッドの安全性を確保することです。CompareAndSet メソッドは操作の 1 つであり、特定のニーズを満たすように設計されており、単一責任の原則に違反しません。単一責任の原則では、クラスまたはモジュールの主な責任は 1 つだけであることが必要であり、AtomicInteger クラスの主な責任はアトミックな整数演算を提供することです。compareAndSet メソッドはその一部であり、クラスの主な責任の一部です。したがって、compareAndSet メソッドは単一責任の原則に違反しません。     

意味のある名前を付ける

次のコードでは、checkParam の戻り値はブール値ですが、読者としては、チェックに合格した場合に True を返す必要があるのか​​、チェックに失敗した場合に True を返す必要があるのか​​わかりません。



一般に、check はメソッド名として最初に使用され、戻り値はなく、チェックが失敗した場合は例外がスローされます。ブール値を返すメソッドは通常 is で始まりますが、別の書き方にすると読みやすくなるか見てみましょう。



意味のある名前を付ける

典型的なのは、フローアレンジメントファイルの条件判定ノードで、0や1などの意味のない数字が使われており、一目で何を言っているのか分かりにくく、一つ一つクリックして確認する必要があります。ロジックを整理して意味のあるものに変更するため、わかりやすい名前を付けます。



クラスとメソッドの命名規則

クラス名には名詞または名詞句が使用され、名前には動詞または動名詞構造が使用されます。

こちらの方が理解しやすいので、今は例を出しません。

無関係なコンテンツを残さないでください

MRを開始する前に、コード内に「コメント化されたコード」とTODOが存在するかどうかを確認してください。

コメントアウトされたコードについては、後から他の開発者が介入すると非常に混乱するでしょう。なぜこれら 2 行のコードをコメントアウトする必要があるのでしょうか? それらは重要ですか?いつか必要になるからそこにあるのでしょうか?それともその時に削除するのを忘れただけでしょうか?それとも将来の修正のためのリマインダーとして機能するのでしょうか? これらの懸念により、他の開発者がコメントされたコードをクリーンアップすることができなくなり、コードが永続的に保持され、「ゴースト コード」が形成される可能性があります。

TODOについては、誰が解決するのか、どのように解決するのか、期限はいつかなど、ERPをフォローしてtodoを書くことをお勧めします。ほとんどの場合、Later は決して に等しくないからです。時間が経つと、私ですらこの todo のこと、当時なぜこの todo を書いたのか、そしてそれにどう対処するか忘れてしまい、システムに隠れた危険が横たわっています。



前述の通り、このTODOを書いた同僚は辞めてしまい、TODOが何なのかは神のみぞ知るとなっています。

列挙する

良い習慣を身につける: 列挙型で定義された変数を使用するときは、他の開発者が読みやすいように列挙型への参照を取得します。

例えば:



これは枯渇可能な変数ですが (状態が制限されているため)、読者は特定の状態を知りません。実際にはそれに関連付けられた列挙があります。



読みやすくするために、変数が定義されている javaDoc の @see/@link メソッドを使用して、この変数の列挙範囲を示すことができます。



単体テスト

単一のテストの重要性は自明のことなので、まずはここで穴を掘り、後で別の記事を書きましょう。

結論

この記事の最後に、古典的な本「コードのクリーンネス」を皆さんにお勧めしたいと思います。実際、この本は以前に出版されたもので、本の知識の一部は少し古いかもしれませんが、最初の数章はまだ読む価値があります。クリーンなコードを実現するには、チームの共同作業が必要です。チーム メンバーは、一貫したコーディング スタイルと標準に従い、コード レビューと知識の共有を実施し、コードの品質を共同で維持および改善する必要があります。それは単なるコーディングスタイルではなく、考え方や価値観ですエレガントなコードは芸術作品のようなもので、タイトルにあるように、ピアニストのように書く必要があります。

著者: JD Retail Tan Lei

出典:JD Cloud Developer Community 転載の際は出典を明記してください

Lei Jun: Xiaomi の新しいオペレーティング システム ThePaper OS の正式版がパッケージ化されました。Gome App の抽選ページのポップアップ ウィンドウは創設者を侮辱しています。Ubuntu 23.10 が正式にリリースされました。金曜日を利用してアップグレードするのもいいでしょう! Ubuntu 23.10 リリース エピソード: ヘイトスピーチが含まれていたため、ISO イメージが緊急に「リコール」されました 23 歳の博士課程の学生が Firefox で 22 年間続いた「ゴーストバグ」を修正しました RustDesk リモート デスクトップ 1.2.3 がリリースされましたWayland を強化して TiDB 7.4 をサポート リリース: MySQL 8.0 と正式互換. Logitech USB レシーバーを取り外した後、Linux カーネルがクラッシュしました. マスターは Scratch を使用して RISC-V シミュレータをこすり、Linux カーネルを正常に実行しました. JetBrains が Writerside ツールを開始しました技術文書の作成に。
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/u/4090830/blog/10120040