Documentation/CodeConventions

コードの書き方に関する一般的なきまり

Red5 のコードの書き方に関するきまりは、次のところで参照できる Sun の Java プログラミング言語用コーディング規約に基づいています:  http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html

Red5 で使用している以下のルールは、Sun の規約とは異なるかもしれません。

  • 1 行の長さは 120 文字以内にします。
  • タブではなくスペースを使用します。
  • インデントの幅はスペース 4 個分とします。
  • 完全修飾のインポート文を使用します。すなわち、アスタリスクは使用しません。
  • 開き中括弧の前には改行を入れません。閉じ中括弧の後には改行を入れます。
    // このように記述します
    public void correctBlock() {
    }
    
    // これは避けます
    public void badBlock() 
    {
    }
    
  • if 文および while 文の条件部分では、真偽の値をテストするのに同等演算子を使用しません。
    // このように記述します
    if (foo)
    
    // これは避けます
    if (foo==true)
    
  • コードでは常に短い形式のクラス名を使用します。ただし、java.sql.Date と java.util.Date が同一のクラス内で使われる場合など、完全修飾クラス名が必要となる例外ケースも存在します。それ以外の場合は、常にクラスをインポートして短い名前を使用する必要があります。
    // このように記述します
    try {
    } catch (IOException e) {
    }
    
    // これは避けます
    try {
    } catch (java.io.IOException e) {
    }
    

Javadoc

メソッドに関するドキュメントを用意するとともに、処理の流れを読み手にわかりやすくするためにメソッド内にもコメントを付けます。内部メソッドのコメントは "//" でブロック化し、複数行にわたる場合でも "/*" は使用しません。バグ報告に対するコードの場合には、コメントにトラッキング番号を付記します。

重要ないし大量のコードをコントリビュートした場合は、@author タグを使ってクラスまたはメソッドに自分の名前とメールアドレス (省略可能) を追加します。こうすることで貢献への認知度が高まるほか、修正をする場合の連絡先を知らせることができます。

あとで作業が必要なものについては、その印として @todo を使用します。

ユニットテスト

すべての新しいクラスとメソッドには、対応するユニットテストを用意する必要があります。

  • ユニットテストには、*Test.java という名前を使用します。
  • public static Test suite() やコンストラクタメソッドを定義しないでください。そのようなことをしなくても、ビルドシステムが自動的に適切な処理を行います。

ロギング

  • Log4j、JCL、または Java ロギング (JULI) ではなく、SLF4J org.slf4j.Logger を使用します。
  • 第三者が問題を見つけることができるよう、必要なだけ情報を記録します。
  • プログラマがスローする throwable は記録せず、呼び出し元に任せます。
  • SLF4J の varargs {} 展開を使用します。
  • ロギング呼び出しにループが含まれる場合には、ループを isDebugEnabled() または isTraceEnabled() ブロックの内部に入れます。

レベル

  • 詳細/診断ロギングには、トレース (trace) レベルを使用します。
  • アプリケーション開発者が知る必要のあるものには、デバッグ (debug) レベルを使用します。
  • 管理者が知る必要のあるものには、情報 (info) レベルを使用します。
  • アプリケーションの問題または一時的な問題を示すものには、警告 (warn) レベルを使用します。
  • サーバー自体の問題を示すものには、エラー (error) レベルを使用します。

例外

  • ある状況が例外的であるのは、プログラムがそれを合理的な努力によって処理することができない場合だけです。不正な入力データは、通常のコードで予想される状況であり、適切に処理することができます。
  • 例外処理の目的は、本当の意味でのエラー処理をコードの通常の部分から分離することです。したがって、呼び出し元に不必要な例外を処理させることは避けなければなりません。
  • パラメータが無効であるなど、コードで本当に処理を続行できない問題がある場合は、例外をスローすることに何も問題はありません。
  • 特別な結果が帰るとコードの呼び出し元で問題が起きるのではないか、と想像で思うだけの場合は、例外をスローしないでください。代わりに、特別な結果、たとえば null を返すようにし、呼び出し元が通常の if-else 文で処理できるようにしてください。呼び出し元で本当に問題があれば、その呼び出し元が自分の判断で例外をスローするでしょう。
  • 実際にはコードに何も問題がなく、例外なしに処理を続行して特別な値を返すことができるにもかかわらず、コードで例外をスローすれば、特別な値が本当にエラーなのかどうかにかかわらず、呼び出し元が行うべき判断をコードで先取りしてしまうことになります。
  • 呼び出し元が自分のコンテキストではエラーでないと判断するような状況で、呼び出されたコードの側で例外をスローすれば、呼び出し元の通常のコードの一部として例外ハンドラを記述させるかアボートさせることになり、結局は、通常のコードへの例外処理の混入を呼び出し元に強いることになります。これは、例外処理の本来の意図とはまったく逆の結果です。
    // よくない例
    java.lang.Class.forName(String) throws ClassNotFoundException
    
  • ほとんどのプログラム/状況では、このメソッドがクラスを見つけられない場合はエラーとなり、したがって例外をスローすると、呼び出し元の判断を先取りしてしまうことになります。
  • しかし、クラスが存在するかどうか、クラスのリストをチェックするプログラムがあるかもしれません。こうしたプログラムでは、本来このプログラムのコンテキストではエラーではないはずの例外の処理を、通常のコードに混入させなければならなくなります。
  • したがって上のメソッドは、例外ではなく、特別な結果の値 null を返すべきです。このメソッドの多くの呼び出し元はこうした状況をすでに想定しているので、これらの呼び出し元では想定外の状況/例外的な状態にはなりません。つまり、呼び出し元の側で状況を判断することができます。
  • 呼び出し元が処理できる可能性がある場合には、(RuntimeException から派生していない) チェックされる例外だけをスローします。
  • プログラミングエラーやシステム障害を示す例外は、通常、実行時には処理/修復できません。これらはチェックされない例外です。
  • パラメータが無効であるなど、コードで本当に処理を続行できない問題がある場合は、(RuntimeException から派生した) チェックされない例外をスローし、チェックされる例外はスローしないでください。なぜなら、コードで問題を処理できないだけでなく、ほとんど場合は呼び出し元でも問題を処理できる可能性がないからです。もしかするとより上位のレイヤのどこかに、すべての RuntimeException をキャッチし、これを記録し、通常のサービスを続行するロジックが存在しているかもしれません。
  • 特別な結果の値をクリーンな形で返すことができない場合にのみ、チェックされる例外を使って、状況の判断を呼び出し元に委ねてください。この場合、呼び出し元の側では、いくつかチェックされる例外をキャッチし、特別な結果の値 (?) などを使ってこれを処理するか、または処理できないエラーなら、チェックされない例外として上に送って、状況を打開することになります。
  • チェックされる例外は、インタフェースの公式な一部です。したがって、チェックされる例外を 1 つの抽象レイヤから別のレイヤへと伝播させないでください。こうすると、通常は下位の抽象レイヤを破壊することになります。たとえば、SQLException はほかのレイヤに伝播させないでください。なぜなら、SQLException は実装の詳細であって、実装の詳細は将来変更されることがあり、こうした変更がインタフェースやその呼び出し元に影響を及ぼしてはならないからです。
  • NullPointerException または RuntimeException は決してスローしないでください。代わりに IllegalArgumentException または NullArgumentException (これは IllegalArgumentException のサブクラスですが) を使ってください。例外を表す適切なサブクラスを利用できない場合は、独自のサブクラスを作成してください。

Eclipse ユーザー

フォーマッタプロファイル

プロファイルは、次のところからダウンロードできます:  red5_codeformat.xml

Window -> Preferences
Java -> Code Style -> Formatter
import をクリックし、上でダウンロードした red5_codeformat.xml ファイルを選択します。
インポートしたら OK をクリックします。

コードテンプレート

必要なヘッダーフォーマットを持った最新バージョンは、次のところからダウンロードできます:  red5_codetemplate.xml

Window -> Preferences
Java -> Code Style -> Code Templates
import をクリックし、上でダウンロードした red5_codetemplate.xml ファイルを選択します。
インポートしたら OK をクリックします。

パッチの提出

Red5 に変更を加えて、プロジェクトにコントリビュートしたいと思ったら、svn でパッチを作成し、このパッチを Red5 のユーザー向けメーリングリストに投稿するか、または Trac 経由で提出する必要があります。パッチを作成するには、次のコマンドを実行します。

$> svn diff > your-changes.patch

注: Eclipse を使ってパッチを作成することもできます (Team -> Create Patch...) が、この場合、プロジェクトレイアウト (ブランチごとのワークスペースか 1 つのワークスペースにすべてのブランチか) に合わせるために、コミッタがパッチを修正しなければならなくなることがあるほか、コミッタによっては Eclipse/Subclipse を使っていないこともあります。