1. 概要

このチュートリアルでは、クリーンなコーディングの原則について説明します。 また、クリーンなコードが重要である理由と、それを実現する方法をJavaで理解します。 さらに、私たちを助けるために利用できるツールがあるかどうかを確認します。

2. クリーンコードとは何ですか?

したがって、クリーンコードの詳細に入る前に、クリーンコードの意味を理解しましょう。 正直なところ、これに対する良い答えは1つではありません。 プログラミングでは、いくつかの懸念が広がり、したがって一般的な原則になります。 しかし、その後、すべてのプログラミング言語とパラダイムは独自のニュアンスのセットを提示します。これにより、適切なプラクティスを採用することが義務付けられます。

大まかに言って、クリーンなコードは、開発者なら誰でも簡単に読み取って変更できるコードとして要約できます。 これは概念を単純化しすぎているように聞こえるかもしれませんが、チュートリアルの後半で、これがどのように構築されるかを説明します。 クリーンなコードについて聞いたところならどこでも、MartinFowlerへの言及に出くわすかもしれません。 彼が場所の1つでクリーンなコードを説明する方法は次のとおりです。

愚か者なら誰でも、コンピューターが理解できるコードを書くことができます。 優れたプログラマーは、人間が理解できるコードを記述します。

3. なぜクリーンなコードを気にする必要があるのですか?

クリーンなコードを書くことは、スキルの問題であると同時に、個人的な習慣の問題でもあります。 開発者として、私たちは時間の経過とともに経験と知識を通じて成長します。 しかし、結局、クリーンなコードの開発に投資する必要があるのはなぜでしょうか。 他の人はおそらく私たちのコードを読みやすいと思うでしょうが、そのインセンティブは十分ですか? 確認してみましょう!

クリーンなコーディングの原則は、私たちが作成しようとしているソフトウェアに関連する多くの望ましい目標を達成するのに役立ちます。 それをよりよく理解するためにそれらを調べてみましょう:

  • 保守可能なコードベース:私たちが開発するソフトウェアはどれも生産的な寿命があり、この期間中は変更と一般的な保守が必要になります。 クリーンなコードは、長期にわたって変更および保守が容易なソフトウェアの開発に役立ちます。
  • より簡単なトラブルシューティング:ソフトウェアは、さまざまな内部または外部の要因により、意図しない動作を示す可能性があります。 多くの場合、修正と可用性の観点から、迅速な対応が必要になる場合があります。 クリーンなコーディング原則で開発されたソフトウェアは、問題のトラブルシューティングが簡単です
  • より高速なオンボーディング:ソフトウェアの存続期間中は、多くの開発者がソフトウェアを作成、更新、および保守し、開発者はさまざまな時点で参加します。 これには、生産性を高く保つためのより迅速なオンボーディングが必要であり、クリーンなコードはこの目標を達成するのに役立ちます。

4. クリーンコードの特徴

クリーンなコーディング原則で記述されたコードベースは、それらを際立たせるいくつかの特徴を示します。 これらの特徴のいくつかを見てみましょう:

  • Focused :特定の問題を解決するには、コードを記述する必要があります。 与えられた問題の解決に厳密に関係のないことは何もしてはいけません。 これは、メソッド、クラス、パッケージ、モジュールなど、コードベースのすべてのレベルの抽象化に適用されます。
  • Simple :これは、クリーンなコードの最も重要で、しばしば無視される特性です。 ソフトウェアの設計と実装は、可能な限り単純である必要があります。これにより、目的の結果を達成できます。 コードベースの複雑さが増すと、エラーが発生しやすくなり、読み取りと保守が困難になります。
  • テスト可能:クリーンなコードは、シンプルでありながら、目前の問題を解決する必要があります。 直感的で、コードベースのテストが簡単である必要があります。できれば自動化された方法で。 これにより、コードベースのベースライン動作を確立し、何も壊さずにコードベースを簡単に変更できるようになります。

これらは、前のセクションで説明した目標を達成するのに役立ちます。 後でリファクタリングする場合と比較して、これらの特性を念頭に置いて開発を開始することは有益です。 これにより、ソフトウェアライフサイクルの総所有コストが削減されます。

5. Javaでのクリーンコーディング

十分な背景を説明したので、Javaにクリーンなコーディング原則を組み込む方法を見てみましょう。 Javaには、クリーンなコードを作成するのに役立つ多くのベストプラクティスが用意されています。 それらをさまざまなバケットに分類し、コードサンプルを使用してクリーンなコードを作成する方法を理解します。

5.1. プロジェクト構造

Javaはプロジェクト構造を強制しませんが、一貫したパターンに従って、ソースファイル、テスト、構成、データ、およびその他のコードアーティファクトを整理することは常に役立ちます。 Java用の人気のあるビルドツールであるMavenは、特定のプロジェクト構造を規定しています。 Mavenを使用しない場合もありますが、規則に固執することは常に良いことです。

Mavenが作成を提案するフォルダーのいくつかを見てみましょう。

  • src / main / java :ソースファイル用
  • src / main / resources :プロパティなどのリソースファイル用
  • src / test / java :テストソースファイル用
  • src / test / resources :プロパティなどのテストリソースファイル用

これと同様に、Java用に提案されたBazel のような他の人気のあるプロジェクト構造があり、ニーズと対象者に応じて1つを選択する必要があります。

5.2. 命名規則

の命名規則に従うと、コードを読みやすく、したがって保守しやすくするのに大いに役立ちます。 Springの作成者であるRodJohnsonは、Springでの命名規則の重要性を強調しています。

「…何かが何をするかを知っていれば、Springクラスの名前またはそのインターフェイスを推測する可能性がかなり高くなります…」

Java は、Javaで何かに名前を付ける場合に準拠する一連のルールを規定しています。 整形式の名前は、コードを読むのに役立つだけでなく、コードの意図について多くのことを伝えます。 いくつか例を見てみましょう。

  • クラス:オブジェクト指向の概念に関するクラスは、実世界のオブジェクトを表すことが多いオブジェクトの青写真です。 したがって、名詞を使用して、それらを十分に説明するクラスに名前を付けることは意味があります。
public class Customer {
}
  • Variables :Javaの変数は、クラスから作成されたオブジェクトの状態をキャプチャします。 変数の名前は、変数の意図を明確に説明する必要があります。
public class Customer {
    private String customerName;
}
public class Customer {
    private String customerName;
    public String getCustomerName() {
        return this.customerName;
    }
}

Javaで識別子に名前を付ける方法についてのみ説明しましたが、読みやすさを確認するために、キャメルケースなどの追加のベストプラクティスがあることに注意してください。 インターフェイス、列挙型、定数の命名に関連する規則がさらに存在する可能性があります。

5.3. ソースファイルの構造

ソースファイルにはさまざまな要素を含めることができます。 Java コンパイラはいくつかの構造を強制しますが、大部分はfluidです。 ただし、ソースファイルに要素を配置する特定の順序に従うと、コードの可読性が大幅に向上します。 Google によるものや、 Spring によるものなど、インスピレーションを得るための人気のあるスタイルガイドは複数あります。

ソースファイル内の要素の一般的な順序がどのように見えるかを見てみましょう。

  • パッケージステートメント
  • ステートメントのインポート
    • すべての静的インポート
    • すべての非静的インポート
  • 正確に1つのトップレベルクラス
    • クラス変数
    • インスタンス変数
    • コンストラクター
    • メソッド

上記とは別に、メソッドは、機能またはスコープに基づいてグループ化できます。 良い慣習はありません。アイデアは一度決定してから一貫して従う必要があります。

整形式のソースファイルを見てみましょう。

# /src/main/java/com/baeldung/application/entity/Customer.java
package com.baeldung.application.entity;

import java.util.Date;

public class Customer {
    private String customerName;
    private Date joiningDate;
    public Customer(String customerName) {
        this.customerName = customerName;
        this.joiningDate = new Date();
    }

    public String getCustomerName() { 
        return this.customerName; 
    }

    public Date getJoiningDate() {
        return this.joiningDate;
    }
}

5.4. 空白

大きなテキストブロックと比較して、短い段落を読んで理解する方が簡単であることは誰もが知っています。 コードの読み取りに関しても、それほど違いはありません。 適切に配置された一貫性のある空白と空白行は、コードの可読性を高めることができます。

ここでのアイデアは、コードに論理的なグループ化を導入することです。これは、思考プロセスを読み通そうとしているときに、思考プロセスを整理するのに役立ちます。 ここで採用する単一のルールはありませんが、ガイドラインの一般的なセットと、読みやすさを中心に保つという固有の意図があります。

  • 静的ブロック、フィールド、コンストラクター、および内部クラスを開始する前の2つの空白行
  • 複数行であるメソッドシグネチャの後の1つの空白行
  • if、for、catchなどの予約済みキーワードを開き括弧から区切る単一のスペース
  • elseのように予約されたキーワードを区切る単一のスペース、閉じ括弧からキャッチ

ここにあるリストは網羅的なものではありませんが、私たちに向かうべき方向性を与えるはずです。

5.5. インデント

非常に些細なことですが、ほとんどすべての開発者は、適切にインデントされたコードがはるかに読みやすく理解しやすいという事実を保証します。 Javaのコードインデントには単一の規則はありません。 ここで重要なのは、一般的な規則を採用するか、プライベートな規則を定義して、組織全体で一貫してそれに従うことです。

重要なインデント基準のいくつかを見てみましょう。

  • 一般的なベストプラクティスは、インデントの単位である4つのスペースを使用することです。 一部のガイドラインでは、スペースではなくタブを提案していることに注意してください。 ここには絶対的なベストプラクティスはありませんが、重要なのは一貫性です。
  • 通常、行の長さには上限がありますが、開発者が現在使用している画面が大きいため、これは従来の80よりも高く設定できます。
  • 最後に、多くの式は1行に収まらないため、一貫してそれらを分割する必要があります。
    • カンマの後にメソッド呼び出しを中断します
    • 演算子の前で式を壊す
    • 読みやすくするために折り返し行をインデントします(ここBaeldungでは2つのスペースを優先します)

例を見てみましょう:

List<String> customerIds = customer.stream()
  .map(customer -> customer.getCustomerId())
  .collect(Collectors.toCollection(ArrayList::new));

5.6. メソッドパラメータ

メソッドが仕様どおりに機能するためには、パラメーターが不可欠です。 ただし、パラメータのリストが長いと、誰かがコードを読んで理解するのが難しくなる可能性があります。 では、どこに線を引くべきでしょうか? 私たちを助けるかもしれないベストプラクティスを理解しましょう:

  • メソッドが受け入れるパラメーターの数を制限してみてください。3つのパラメーターが1つの良い選択になる可能性があります
  • 推奨されるパラメータよりも多くのパラメータが必要な場合は、メソッドのリファクタリングを検討してください。通常、長いパラメータリストは、メソッドが複数のことを実行している可能性があることも示しています。
  • パラメータをカスタムタイプにバンドルすることを検討する場合がありますが、無関係のパラメータを単一のタイプにダンプしないように注意する必要があります
  • 最後に、この提案を使用してコードの可読性を判断する必要がありますが、それについて熟慮してはなりません。

この例を見てみましょう:

public boolean setCustomerAddress(String firstName, String lastName, String streetAddress, 
  String city, String zipCode, String state, String country, String phoneNumber) {
}

// This can be refactored as below to increase readability

public boolean setCustomerAddress(Address address) {
}

5.7. ハードコーディング

コードに値をハードコーディングすると、多くの場合、複数の副作用が発生する可能性があります。 たとえば、重複が発生する可能性があり、変更がより困難になります。 値を動的にする必要がある場合、望ましくない動作につながることがよくあります。 ほとんどの場合、ハードコードされた値は、次のいずれかの方法でリファクタリングできます。

  • Java内で定義された定数または列挙型に置き換えることを検討してください
  • または、クラスレベルまたは別のクラスファイルで定義された定数に置き換えます
  • 可能であれば、構成または環境から選択できる値に置き換えてください

例を見てみましょう:

private int storeClosureDay = 7;

// This can be refactored to use a constant from Java

private int storeClosureDay = DayOfWeek.SUNDAY.getValue()

繰り返しになりますが、これを守るための厳密なガイドラインはありません。 しかし、後でこのコードを読んで維持する必要がある人もいるという事実を認識している必要があります。 私たちは自分に合った慣習を選び、それについて一貫している必要があります。

5.8. コードコメント

コードコメントは、コードを読んでいるときに有益であり、重要な側面を理解することができます。 同時に、コメントに明白なものを含めないように注意する必要があります。 これによりコメントが肥大化し、関連する部分が読みにくくなる可能性があります。

Javaでは、実装コメントとドキュメントコメントの2種類のコメントを使用できます。 それらには、異なる目的と異なる形式もあります。 それらをよりよく理解しましょう:

  • ドキュメント/JavaDocコメント
    • ここでの聴衆はコードベースのユーザーです
    • ここでの詳細は通常、実装が不要であり、仕様に重点を置いています。
    • 通常、コードベースに関係なく便利です
  • 実装/ブロックコメント
    • ここでの聴衆は、コードベースに取り組んでいる開発者です
    • ここでの詳細は実装固有です
    • 通常、コードベースと一緒に役立ちます

では、それらを有用で状況に応じて使用できるように、どのように最適に使用する必要がありますか?

  • コメントはコードを補完するだけで、コメントなしでコードを理解できない場合は、おそらくリファクタリングする必要があります
  • ブロックコメントはめったに使用しないでください。重要な設計上の決定を説明するために使用する可能性があります。
  • ほとんどのクラス、インターフェイス、パブリックメソッドおよび保護されたメソッドにJavaDocコメントを使用する必要があります
  • すべてのコメントは、読みやすくするために適切なインデントを付けて整形式にする必要があります

意味のあるドキュメントコメントの例を見てみましょう。

/**
* This method is intended to add a new address for the customer.
* However do note that it only allows a single address per zip
* code. Hence, this will override any previous address with the
* same postal code.
*
* @param address an address to be added for an existing customer
*/
/*
* This method makes use of the custom implementation of equals 
* method to avoid duplication of an address with same zip code.
*/
public addCustomerAddress(Address address) {
}

5.9. ロギング

デバッグのために本番コードに手を置いたことがある人は、ある時点でより多くのログを望んでいます。 ログの重要性は、開発全般、特にメンテナンスで強調しすぎることはありません。

Javaには、SLF4J、Logbackなど、ロギング用のライブラリとフレームワークがたくさんあります。 コードベースでのロギングは非常に簡単ですが、ロギングのベストプラクティスには注意を払う必要があります。 それ以外の方法で行われたロギングは、助けではなく、メンテナンスの悪夢であることが判明する可能性があります。 これらのベストプラクティスのいくつかを見ていきましょう。

  • 過度のログ記録を避け、トラブルシューティングに役立つ情報を検討してください
  • ログレベルを賢く選択してください。本番環境でログレベルを選択的に有効にすることができます。
  • ログメッセージのコンテキストデータを非常に明確かつ説明的にする
  • ログメッセージのトレース、集約、フィルタリングに外部ツールを使用して、分析を高速化します

適切なレベルの記述的ロギングの例を見てみましょう。

logger.info(String.format("A new customer has been created with customer Id: %s", id));

6. それで全部ですか?

前のセクションではいくつかのコード形式の規則に焦点を当てていますが、知っておく必要があるのはこれらだけではありません。 読み取り可能で保守可能なコードは、時間の経過とともに蓄積された多数の追加のベストプラクティスから恩恵を受けることができます。

時間の経過とともに、面白い頭字語としてそれらに遭遇した可能性があります。 それらは基本的に、より良いコードを書くのに役立つ単一または一連の原則として学習をキャプチャします。 ただし、それらが存在するという理由だけでそれらすべてをフォローするべきではないことに注意してください。 ほとんどの場合、それらが提供する利点は、コードベースのサイズと複雑さに比例します。 原則を採用する前に、コードベースにアクセスする必要があります。 さらに重要なことに、私たちはそれらとの一貫性を維持しなければなりません。

6.1. 個体

SOLIDは、理解可能で保守可能なソフトウェアを作成するための5つの原則から派生したニーモニックの頭字語です。

  • 単一責任の原則:定義するすべてのインターフェース、クラス、またはメソッドには、明確に定義された目標が必要です。 本質的に、それは理想的には1つのことを実行し、それをうまく実行する必要があります。 これにより、テスト可能なメソッドとクラスが効果的に小さくなります。
  • オープンクローズ原則:私たちが書くコードは、理想的には拡張のためにオープンであるが、変更のためにクローズされている必要があります。 これが効果的に意味するのは、クラスを変更する必要がないようにクラスを作成する必要があるということです。 ただし、継承または構成による変更を許可する必要があります。
  • Liskov置換原則:この原則が述べているのは、すべてのサブクラスまたは派生クラスは、親クラスまたは基本クラスの代わりに使用できる必要があるということです。 これは、コードベースの結合を減らし、全体の再利用性を向上させるのに役立ちます。
  • インターフェイス分離の原則:インターフェイスの実装は、クラスに特定の動作を提供する方法です。 ただし、クラスは、を必要としないメソッドを実装する必要はありません。 これが私たちに必要なことは、より小さく、より焦点を絞ったインターフェースを定義することです。
  • 依存性逆転の原則:この原則によれば、クラスは抽象化のみに依存し、具体的な実装には依存しない。 これは事実上、クラスが依存関係のインスタンスの作成を担当してはならないことを意味します。 むしろ、そのような依存関係をクラスに注入する必要があります。

6.2. DRY&KISS

DRYは「Don’sRepeatYourself」の略です。 この原則は、コードの一部をソフトウェア全体で繰り返さないようにすることを示しています。 この原則の背後にある理論的根拠は、重複を減らし、再利用性を高めることです。 ただし、これを文字通りに採用する場合は注意が必要です。 一部の複製は、実際にコードの可読性と保守性を向上させることができます。

KISSは「KeepItSimple、Stupid」の略です。 この原則は、コードをできるだけ単純に保つように努めるべきであると述べています。 これにより、長期にわたる理解と保守が容易になります。 前述のいくつかの原則に従って、クラスとメソッドに焦点を合わせて小さくしておくと、コードが単純になります。

6.3. TDD

TDDは「テスト駆動開発」の略です。 これは、自動テストが失敗した場合にのみコードを書くように要求するプログラミング手法です。 したがって、自動テストの設計開発から始める必要があります。 Javaには、JUnitやTestNGなどの自動化された単体テストを作成するためのフレームワークがいくつかあります。

そのような実践の利点は途方もないです。 これにより、常に期待どおりに機能するソフトウェアが実現します。 常にテストから開始するため、作業コードを小さなチャンクで段階的に追加します。 また、新しいテストまたは古いテストのいずれかが失敗した場合にのみコードを追加します。 つまり、再利用性にもつながります。

7. ヘルプ用のツール

クリーンなコードを書くことは、原則と実践の問題であるだけでなく、個人的な習慣でもあります。 私たちは学び、適応するにつれて、より優れた開発者として成長する傾向があります。 ただし、大規模なチーム全体で一貫性を維持するために、いくつかの強制を実践する必要もあります。 コードレビューは、一貫性を維持し、建設的なフィードバックを通じて開発者の成長を支援するための優れたツールです。

ただし、コードレビュー中にこれらすべての原則とベストプラクティスを手動で検証する必要はありません。 JavaOffHeapのFreddyGuimeは、品質チェックの一部を自動化して、常にコード品質で特定のしきい値を達成することの価値について語っています。

Javaエコシステムで利用可能ないくつかのツールがあり、コードレビューアからこれらの責任の少なくとも一部を取り除きます。 これらのツールのいくつかを見てみましょう:

  • コードフォーマッター:EclipseやIntelliJを含む人気のあるJavaコードエディターのほとんどは、自動コードフォーマットを可能にします。 デフォルトのフォーマットルールを使用したり、カスタマイズしたり、カスタムフォーマットルールに置き換えたりすることができます。 これにより、多くの構造コード規則が処理されます。
  • 静的分析ツール: SonarQube Checkstyle PMD SpotBugs など、Java用の静的コード分析ツールがいくつかあります。 それらには、そのまま使用することも、特定のプロジェクト用にカスタマイズすることもできる豊富なルールのセットがあります。 これらは、命名規則の違反やリソースリークなど、多くのコードの臭いを検出するのに最適です。

8. 結論

このチュートリアルでは、クリーンなコーディングの原則と、クリーンなコードが示す特性の重要性について説明しました。 Javaで開発されたこれらの原則のいくつかを実際に採用する方法を見ました。 また、コードを長期にわたって読み取り可能で保守しやすい状態に保つのに役立つ他のベストプラクティスについても説明しました。 最後に、この取り組みに役立ついくつかのツールについて説明しました。

要約すると、これらの原則と実践のすべてがコードをよりクリーンにするためにあることに注意することが重要です。 これはより主観的な用語であるため、状況に応じて評価する必要があります。

採用できるルールのセットは多数ありますが、成熟度、文化、および要件を意識する必要があります。 カスタマイズする必要があるかもしれませんし、そのことについては、まったく新しいルールのセットを考案する必要があるかもしれません。 ただし、どのような場合でも、メリットを享受するには、組織全体で一貫性を保つことが重要です。