この記事では、私の周りの同僚の Java コードで見られる典型的なエラーをいくつかリストします。明らかに、静的コード分析 (私たちのチームは qulice を使用しています) ではすべての問題を見つけることはできないため、ここでそれらをリストします。
何かが足りないと思われる場合は、お知らせください。喜んで追加させていただきます。
以下にリストされているこれらのエラーはすべて、基本的にオブジェクト指向プログラミング、特に Java の OOP に関連しています。
クラス名
この短い記事「オブジェクトとは何か」を読んでください。クラスは実際には抽象的なエンティティである必要があり、「バリデータ」、「コントローラ」、「マネージャ」ではありません。クラス名が「er」で終わる場合、それは悪い設計です。
もちろん、Apache の StringUtils、FileUtils、IOUtils などのツール クラスもアンチパターンです。上記はすべて悪い設計の例です。詳細情報: OOP のツール クラスの代替案。
もちろん、クラスとインターフェイスを区別するためにプレフィックスやサフィックスを使用しないでください。たとえば、IRecord、IfaceEmployee、または RecordInterface などの名前は間違っています。一般に、インターフェイス名は現実のエンティティの名前である必要があり、クラス名はその実装の詳細を説明する必要があります。実装に特別な点がない場合は、デフォルト、シンプル、または類似したものと呼ぶことができます。例えば:
次のようにコードをコピーします。
クラス SimpleUser はユーザー {} を実装します。
class DefaultRecord は Record {} を実装します。
class サフィックスは名前 {} を実装します。
クラス Validated は Content {} を実装します。
メソッド名
メソッドは値または void を返すことができます。メソッドが値を返す場合、その名前は、たとえば、返される内容を説明する必要があります (get プレフィックスは絶対に使用しないでください)。
次のようにコードをコピーします。
ブール値 isValid(文字列名);
文字列コンテンツ();
int ageOf(ファイル file);
void を返す場合は、その名前でその動作を説明する必要があります。例えば:
次のようにコードをコピーします。
void save(ファイル file);
void プロセス(作業 work);
void append(ファイル file, 文字列行);
先ほど述べたルールには例外が 1 つだけあります。JUnit のテスト メソッドは考慮されません。これについては以下で説明します。
テストメソッドの名前
JUnit テスト ケースでは、メソッド名はスペースを含まない英語のステートメントである必要があります。例を見てみると、より明確になります。
次のようにコードをコピーします。
/**
* HttpRequest はそのコンテンツを Unicode で返すことができます。
* @throws テストが失敗した場合の例外
*/
public void returnsItsContentInUnicode() が例外をスローする {
}
JavaDoc の最初の文は、テストするクラスの名前で始まり、その後に can が続く必要があります。したがって、最初の文は「somebody can do something」のようなものにする必要があります。
メソッド名も同じですが、テーマがありません。メソッド名の途中に件名を追加すると、上記の例のように、「HttpRequest はコンテンツを Unicode で返します」という完全な文が得られます。
テストメソッドの名前が can で始まらないことに注意してください。 JavaDoc 内のコメントのみ can で始まります。また、メソッド名は動詞で始めてはなりません。
実際には、例外をスローするテスト メソッドを宣言することが最善です。
変数名
timeOfDay、firstItem、httpRequest などの変数名を組み合わせないでください。これは、クラス変数とメソッド内の変数に当てはまります。変数名は、表示範囲内での曖昧さを避けるために十分な長さである必要がありますが、可能であれば長すぎないようにします。名前は、単数形または複数形の名詞、あるいは適切な略語である必要があります。例えば:
次のようにコードをコピーします。
リスト<String> 名。
void sendThroughProxy(ファイル file, プロトコル proto);
プライベートファイルの内容。
パブリック HttpRequest リクエスト。
場合によっては、コンストラクターが入力パラメーターを新しく初期化されたオブジェクトに保存すると、そのパラメーターの名前とクラス属性が競合することがあります。この場合、私の提案は、母音を削除して略語を使用することです。
例:
次のようにコードをコピーします。
パブリック クラス メッセージ {
プライベート文字列受信者。
public Message(String rcpt) {
this.recipient = rcpt;
}
}
多くの場合、変数のクラス名を見れば、その変数にどのような名前を付ける必要があるかがわかります。次のように信頼性の高い小文字形式を使用してください。
次のようにコードをコピーします。
ファイルファイル;
ユーザー ユーザー;
支店支店。
ただし、整数や文字列などのプリミティブ型では決してこれを実行しないでください。
性質の異なる複数の変数がある場合は、形容詞の使用を検討してください。例えば:
次のようにコードをコピーします。
ストリングコンタクト(ストリング左、ストリング右);
コンストラクタ
例外を無視して、データをオブジェクト変数に格納するために使用されるコンストラクターは 1 つだけである必要があります。他のコンストラクターは、異なるパラメーターを使用してこのコンストラクターを呼び出します。例えば:
次のようにコードをコピーします。
パブリック クラス サーバー {
プライベート文字列アドレス。
パブリックサーバー(文字列uri) {
this.address = uri;
}
パブリックサーバー(URI uri) {
this(uri.toString());
}
}
ワンタイム変数
使い捨て変数は絶対に避けるべきです。ここで「ワンタイム」とは、一度だけ使用される変数のことを意味します。たとえば、これは次のとおりです。
次のようにコードをコピーします。
文字列名 = "data.txt";
新しいファイル(名前)を返します;
上記の変数は 1 回だけ使用されるため、このコードは次のように再構成できます。
次のようにコードをコピーします。
新しいファイルを返します("data.txt");
まれに、主に見栄えを良くするために、使い捨て変数が使用されることがあります。ただし、これはできる限り避けるべきです。
異常な
言うまでもなく、例外を自分で飲み込むべきではなく、可能な限り例外を渡す必要があります。プライベート メソッドは常にチェックされた例外をスローする必要があります。
フロー制御に例外を使用しないでください。たとえば、次のコードは間違っています。
次のようにコードをコピーします。
整数サイズ;
試す {
サイズ = this.fileSize();
} catch (IOException ex) {
サイズ = 0;
}
では、IOException によって「ディスクがいっぱいです」というプロンプトが表示された場合はどうすればよいでしょうか?ファイルサイズが 0 であると考えて処理を続行しますか?
インデント
インデントに関しては、主なルールは、開き括弧が行末で終了するか、同じ行上で閉じられることです (閉じ括弧の場合はその逆です)。たとえば、次の例は、最初の左括弧が同じ行で閉じられておらず、その後に他の文字があるため、正しくありません。 2 番目の括弧も、その前に文字が付いていますが、対応する左括弧が同じ行にないため、問題があります。
次のようにコードをコピーします。
最終ファイル file = 新しいファイル(ディレクトリ,
"ファイル.txt");
正しいインデントは次のようになります。
次のようにコードをコピーします。
StringUtils.join(
Arrays.asList(
「最初の行」、
「2行目」、
StringUtils.join(
Arrays.asList("a", "b")
)
)、
「セパレーター」
);
インデントに関する 2 番目の重要なルールは、1 行にできるだけ多くの文字を同時に書くように努めることです。上限は 80 文字です。上記の例はこの点を満たしていないため、縮小することもできます。
次のようにコードをコピーします。
StringUtils.join(
Arrays.asList(
「1行目」、「2行目」、
StringUtils.join(Arrays.asList("a", "b"))
)、
「セパレーター」
);
冗長定数
クラス定数は、クラスのメソッド内で情報を共有する場合に使用する必要があります。情報はクラスに固有である必要があります。文字列または数値リテラルの代わりに定数を使用しないでください。これは非常に悪い習慣であり、コードを汚染します。定数 (OOP の他のオブジェクトと同様) は、現実世界では独自の意味を持つ必要があります。これらの定数が実際の生活で何を意味するのかを見てみましょう。
次のようにコードをコピーします。
クラスドキュメント {
private static Final String D_LETTER = "D";
private static Final String EXTENSION = ".doc"; // 良い方法
}
もう 1 つのよくある間違いは、テスト メソッドでの冗長な文字列または数値リテラルを避けるために単体テストで定数を使用することです。これは行わないでください。各テスト メソッドには独自の入力値が必要です。
新しいテストメソッドごとに新しいテキストまたは値を使用します。それらは互いに独立しています。では、なぜ依然として同じ入力定数を共有しているのでしょうか?
テストデータ結合
テスト メソッドでのデータ結合の例を次に示します。
次のようにコードをコピーします。
ユーザー user = new User("Jeff");
// ここに他のコードがあるかもしれません
MatcherAssert.assertThat(user.name(), Matchers.equalTo("Jeff"));
最後の行では、「Jeff」が最初の行と同じ文字列リテラルに結合されています。数か月後、誰かが 3 行目の値を変更したい場合は、同じメソッドで「Jeff」がどこで使用されているかを見つけるために時間を費やす必要があります。
これを避けるには、変数を導入することをお勧めします。