■ はじめに 可読性&信頼性を上げるためのJavaのコーディング規約&作法&心構えです。 以下にご注意の上、ご活用ください。 ・包括的に述べたものではない. ・他は一般常識、自分のルール、プロジェクトのルールに従うこと. ・偏屈(笑)なものもあるので、吟味・納得したうえで使用すること. ・「ルールAを実施すればルールBは不要あるいは不可能」なものは含まれている. ・「インデントは....」などと言ったありがちなものは入っていない.
■ ルール まずはルールのみを提示する。解説は後を参照。 ◆ 変数・定数 ◇ boolean(Boolean)は引数には使用しない⇒enumを使用する ◇ boolean(Boolean)は再定義して使用する ◇ boolean(Boolean)は否定形の名称は使わない ◇ できるだけStringは使用しない⇒String内包のクラスにする ◇ nullは再定義して使用する⇒クラス名+IS_NULL ◇ int定数は使用しない⇒enumを使用する. ◇ int定数はInteger変数は使用しない⇒==判定のバグを避けるため. ◇ 離散状態を表すString定数は使用しない⇒enumを使用する ◇ 事前に定義され実行時に変化しないものはListではなく配列にする、 ロジックで生成するものは配列ではなくListにする ◇ 定数をハードコーディングしない. ◇ 変数の使いまわしは避ける ◇ 変数への再代入をなるべく避ける ◇ 型パラメータは先頭に$をつける ◇ 業務固有の名称をつける必要がない場合は、変数の型を連想する名称とする ◆ クラス定義 ◇ インスタンスクラス(仮称:データを入れるクラス)は フィールドをまとめて先頭に記述しフィールドコメントは一行にする. ◇ ロジッククラス(仮称:ロジック中心のクラス)はフィールドは最後に記述する ◇ classのpublicは必要な時だけつける ◇ コンストラクタでは、実質的な処理は行わずフィールドの初期化くらいにする ◇ フィールドの初期化はメソッドにしてfinalをつける. ◇ フィールドの初期化メソッド名はシステム内で統一しておく(initなど) ◇ コンストラクタやメソッドで処理がないときは「処理なし」と記述する ◇ インスタンスクラスはComparatorを作成する ◇ 複数のロジッククラスから利用されるインスタンスクラスは インターフェースを分けることも考える. ◇ 定数だけのインターフェースを作成しない ⇒クラスにする⇒implementsされるとわからない ◇ staticブロックで例外を発生せざるを得ない場合⇒ログ&RuntimeException ◇ 必要に応じてクラス内にローカルなクラスを作成する ◆ 実装・継承 ◇ 標準ライブラリのComparator,Iteratorなどはダミーを作成し、それを使用する ◇ 実装、継承では元の名前を先頭につけた名前にする ◇ 基底クラスのメソッドを使わせたくない時はRuntimeExceptionにする ◇ クラス定義やメソッドには必要な時にはfinalつける ◇ インスタンスクラスはtoStringを実装する ◇ インスタンスクラスはequals,hashCode,Comparable,cloneを実装する. ◆ メソッド ◇ メソッドの名称に否定形は使用しない ◇ Listなどの返値は値がない時はemtpy(サイズがゼロ)よりもnullを戻す ◇ 返値が正常かどうかを判定するstaticメソッドを元のクラスに作る ◇ 返値がbooleanのメソッドは、if文に使用して馴染む名前にする ◇ オブジェクト内部のList,Set,Mapなどはメソッドの戻り値にしない 必要ならばコピーを戻す ◇ 複数の返値を戻したいときにどうするか⇒クラスを作る ◇ 複数の返値を戻したいときにどうするか ⇒オブジェクト配列を戻す⇒値の取出しstaticメソッドを作成する ◇ 内部データが不必要なメソッドはstaticにする ◇ Overloadはなるべくしない⇒ソースが分かりやすくなる ◆ ロジック ◇ 処理スピードよりも分かりやすいプログラミングを心がける ◇ ロジックが「難しい、ややこしい」と思ったら、一度は考え直す ◇ まとまった処理はブロックにする ◇ else文では改行する ◇ 三項演算子を使う ◇ if文の内容がbreak,continue,return,throwだけであれば、 if文と同一行に記述する ◇ Stringの等値判定はequalsを使用する ◇ Stringの等値判定は定数を主語にする ◇ Booleanはnewしない ◇ nullは型を明示する ◇ if,for,whileは一文でもブロックにする ◇ 順番を意識するループでは拡張for文を使用しない ◇ 分岐(if,switch)を使わずに定数テーブルで切り分けることも考える ◇ elseで処理がない時は「処理なし」のコメントをしておく ◇ if文の複合条件を分解して、ネストにする ◇ 複合条件は括弧で分離・明示する ◇ オブジェクトをできるだけ使いまわししない ◇ staticメソッド呼び出しは必ずクラス名にする. 変数名にはしない.自クラスでもクラス名を書く、 ◇ StringBuilder(StringBuffer)はできるだけ使わない ◇ 単体テストしやすいようにメモリ上のロジックをメソッドに外だしする ◇ List,Set,Mapなどに下位オブジェクト追加するときは、 下位オブジェクトnew直後に追加する ◇ 例外処理ではfinalyは必ずつける、原則catchはしない ◆ 共通クラス ◇ 日時処理は共通クラスを作成する⇒フォーマットの統一、日時処理の統一 ◇ リフレクション、通信、IOなどは共通クラスを作成する ⇒他のロジックと混在を避ける ◇ メソッドの引数の数値の配列はprimitiveではなくwrapperにする ◇ クラス名やメソッド名を分かりやすくするために派生クラスを作成する ◇ プロジェクト内の共通クラスの整備は、コストアップになるが、推進すべき ◇ 企業内であれば、長期的展望をもって、整備していくべき
■ 解説 ◆ 変数・定数 ◇ boolean(Boolean)は引数には使用しない⇒enumを使用する booleanはメソッドの引数にした時に分かりにくい. メソッドの仕様を確認しないと分からない. enumにすれば意味が分かりやすい. メソッドの中でのローカルな使用やメソッドの返値にするのはかまわない. ◇ boolean(Boolean)は再定義して使用する boolean isActive = true; 上記よりも、以下の方が分かりやすい. boolean ACTIVE = true; boolean IN_ACTIVE = false; boolean status = IN_ACTIVE; メソッドの引数にした場合も意味が分かる しかし違う意味のbooleanを渡してもコンパイルエラーにはならない 本当はenumがよい. ◇ boolean(Boolean)は否定形の名称は使わない 理由はソースを読んでいて分かりにくい. 否定条件にした時に特に分かりにくい. 他の条件と複合した時に分かりにくい. boolean notFound = true; if (! notFound) { // ←分かりにくい // 見つかった時の処理. } ◇ できるだけStringは使用しない⇒String内包のクラスにする 理由はStringでは分かりにくいから. 名前もString,住所もString,会社名もString,部署名もStringでは分からない. 変な代入をしても、引数を間違えてもコンパイルエラーにならない. 本当はclass CompanyName extends String{}としたいができない ⇒Stringはfinalなので. なのでclass CompanyName { private String value; ...}とする. しかしすべての局面で、これをやろうとすると、かなり面倒. Stringのメソッドを(必要なものだけでよいが)再定義する必要がある. 外部のライブラリを使うときに、常に変換・逆変換をする必要がある. なので、自システムに閉じているものは、String内包クラスを作成する。 外部ライブラリと受け渡すものはStringで我慢する。 ◇ nullは再定義して使用する⇒クラス名+IS_NULL nullは代入する時や、メソッドに渡す時に、意味が分からない. 特にメソッドに渡す時. class Address { // 処理省略 } class MyClass { public void method(Address addr) { // 処理省略 } } // これでは、何のnullを渡したのか分かりにくい MyClass myObj = new MyClass(); myObj.method(null); // ⇒ 分からない // 以下のようにするとよい Address Address_IS_NULL = (Address)null; myObj.method(Address_IS_NULL); // ⇒ 分かりやすい 注、null定義は、本来はクラス(この例ではAddress)の中に定義した方が良い Address.IS_NULLと引用する しかし自分の担当ではない時は、上記のようにする. ◇ int定数は使用しない⇒enumを使用する. intであれば、不正な値が代入されてもチェックされない. しかし既存システムでint定数を使用していればint定数の使用もやむを得ない. // intでの性別定義 final int GENDER_MALE = 1; final int GENDER_FEMALE = 2; int intGender = GENDER_MALE; intGender = 100; // ⇒ コンパイラエラーにならない // 本当は上記よりもenumにする方がよい Gender enumGender = Gender.MALE; ◇ int定数はInteger変数は使用しない⇒==判定のバグを避けるため. // これはfalseになる new Integer(0) == new Integer(0) // これでバグったら分からない. ◇ 離散状態を表すString定数は使用しない⇒enumを使用する これもint定数を使用しないのと同じ.不正な値が代入されてもチェックされない しかし既存システムでString定数を使用していれば、やむを得ない. final String GENDER_MALE = "male"; final String GENDER_FEMALE = "female"; String stringGender = GENDER_FEMALE; stringGender = "abc"; // ⇒ コンパイルエラーにならない // 上記よりもenumにする Gender enumGender = Gender.FEMALE; 補足. DB上では数値または文字でメモリ上でenumであれば相性が悪い ⇒ 相互変換する必要がある. 当該enumにstaticのfromInt(fromString)を実装し、 toInt(toString)を実装すれば、 変換を行う汎用クラスが作成できる. ◇ 事前に定義され実行時に変化しないものはListではなく配列にする、 ロジックで生成するものは配列ではなくListにする 配列にするかListにするかは悩む時があるが、原則を決めておくとよい. ◇ 定数をハードコーディングしない. 単に定数を書いたら、意味が分からない. 修正する時に、同じ値の別の部分を修正してしまう可能性がある. // これでは意味が分からない、 // statusの1を他の値に変更する時にgenderも間違えて変更するかも. int status = 1; // 意味が分からない int gender = 1; // 意味が分からない // 以下のようにすると、分かりやすくなり、変更するにも間違いがなくなる. final int STATUS_ACTIVE = 1; final int STATUS_INSCTIVE = 2; final int GENDER_MALE = 1; final int GEDNER_FEMALE = 2; status = STATUS_ACTIVE; // 分かりやすい gender = GENDER_MALE; // 分かりやすい 注、この例は、本当はenumに変えた方がよい ◇ 変数の使いまわしは避ける ひとつの変数は、一つの意味だけに使用すべき. 忙しい時は間違うこともありえないわけではないので注意する. ◇ 変数への再代入をなるべく避ける 変数に複数回代入していると、その変数を使って処理する時に、 値が分かりにくくなる. 何をもって「再代入」とするかは微妙.例えば以下のものは、問題ないだろう. ループでインスタンスを次々と処理する時に、 インスタンス変数や関連変数に値を代入する. 初期値を設定し何らかの条件で、その値を更新する、 その後、その変数を使って処理する. ダメな場合の例. 変数に値を代入後に、その変数を使って処理し、 さらに値に代入して、またその変数を使って処理する. 「まとまった処理はブロックにする」を参照. ◇ 型パラメータは先頭に$をつける 意図は型パラメータであることを分かりやすくするため 上記意図が実現できれば、他の方式でもよいが、 このあたりが他のルールと抵触しない ◇ 業務固有の名称をつける必要がない場合は、変数の型を連想する名称とする Stringであれば、strなど 特に、共通クラスでは、このようにするとよい ◆ クラス定義 ◇ インスタンスクラス(仮称:データを入れるクラス)は フィールドをまとめて先頭に記述しフィールドコメントは一行にする. 理由はフィールドの一覧をすばやく把握するため. インスタンスクラスでは、フィールドの他にはsetter/getterなど 決まりきったメソッドが定義されている. ソースを見る時には、どのようなフィールドがあるかを 先頭部分を見て一瞬にして把握できるようにしたい. なのでフィールド定義だけを先頭部分に記述し、 フィールドのコメントも改行しないで一行に記述する. ◇ ロジッククラス(仮称:ロジック中心のクラス)はフィールドは最後に記述する ロジッククラスでは、publicなメソッドを把握したい. 内部処理に使うフィールドは、クラスを利用する立場からは不要なので、 最後に記述する. publicな変数・定数は、先頭に記述すべきだが、 privateな定数は最後に記述したほうが良い. ◇ classのpublicは必要な時だけつける 明らかに自パッケージ外から参照されるものは、publicにする. 以外のクラスは(とりあえず)publicはつけない. もし必要になれば、その時点でpublicにするが、 その時に「これはpublicにすべきか否か?」と考えるタイミングが生まれる. 弊害としては、他のパッケージの担当者が、使えるクラスの存在を知らずに、 同じようなクラスを作ってしまうことがありうる. ◇ コンストラクタでは、実質的な処理は行わずフィールドの初期化くらいにする 必須ではないが、常識だろう. 普通コンストラクタでは、あまり処理は入れないので、 他の何かの処理を入れると、他の人が誤解する可能性がある. ◇ フィールドの初期化はメソッドにしてfinalをつける. フィールドは宣言行で初期化しない.初期化メソッドの中でやる. 何を初期化しているか分かりやすくなる. 再初期化が必要になった時に便利. finalにするのはオーバーライドを防ぐため. コンストラクタで呼び出すメソッドをオーバーライドするとおかしくなる. 詳細省略. 注、継承したクラスの初期化メソッドの名称は別途考える必要がある. ◇ フィールドの初期化メソッド名はシステム内で統一しておく(initなど) 誤解を防ぐためには当然のこと. 他の目的のメソッド名を、初期化メソッドと同じにしないこと. ◇ コンストラクタやメソッドで処理がないときは「処理なし」と記述する コンストラクタ 処理がないときは、その旨を明示するのが正しい. ただし、フィールドの初期化をメソッド化していれば処理はあるはず. 継承したクラスのコンストラクタは処理がないかも 「処理が必要か否かを考えた上で、必要がないと判断した」ことが分かる. メソッド 処理がないメソッドがありうるか? ⇒メソッドをオーバーライドした場合にはありうる. 「処理なし」と記述しておく⇒この場合に記述することは重要. 「処理が必要か否かを考えた上で、必要がないと判断した」ことが分かる. ◇ インスタンスクラスはComparatorを作成する 必要がない場合も多いが、一応「作成する」という原則にしておく. 個別判断で「このクラスでは不要」とすればよい. ◇ 複数のロジッククラスから利用されるインスタンスクラスは インターフェースを分けることも考える. 他のロジッククラスに、インターフェースで渡すことも考える 受け渡したクラスに勝手なことをさせない. 受け渡したクラスが何をすべきかはっきり分かる. そのためだけにインターフェースを作成するとよい(かも). ◇ 定数だけのインターフェースを作成しない ⇒クラスにする⇒implementsされるとわからない 理由は上記の通り.クラスであればクラス名を記述するので、 何の定数か、どこで定義されているかわかりやすくなる. ◇ staticブロックで例外を発生せざるを得ない場合⇒ログ&RuntimeException 注意、これはかなり例外的な場合. 共通クラスにstaticな定数があって、それ初期化するときに、 staticブロックを使うと便利. 最初に使われたタイミングで実行されるので、 実行タイミングを考慮する必要がない しかしstaticブロックでは例外を出せない(←コンパイルエラーになる). どうしてもstaticブロックの処理で例外がでる場合は、 内部でcatchしたうえで、ログを出してRuntimeExceptionをthrowすれば、 コンパイルは通る ◇ 必要に応じてクラス内にローカルなクラスを作成する 他で使用しないクラスは、ローカルで定義する. 外に出すと目障り、誤解のもと. 変数をできるだけ、狭いスコープに制限するとの同じ. ◆ 実装・継承 ◇ 標準ライブラリのComparator,Iteratorなどはダミーを作成し、それを使用する 理由はComparatorの一覧がJavaDocですぐに分かるから . // 以下のようにする. interface OurComparator extends Comparator {} class OurClass implements OurComparator { .... } ◇ 実装、継承では元の名前を先頭につけた名前にする 理由は次の通り 名前で何を実装・継承しているか、すぐに分かる Eclipseで隣に表示される. // 例えば次のようにする. /** 社員. / class Staff {} /* 管理職. */ class StaffManager extends Staff{} 問題点 英語的にはしっくりこない名称になる. 複数の実装・継承がある場合には、一つに限定する必要がある. 階層が複数になる場合は、名前がだんだん長くなる. ◇ 基底クラスのメソッドを使わせたくない時はRuntimeExceptionにする 派生クラスを作ったとき、基底クラスのメソッドが不要になることがある. そのメソッドをオーバーライドしてRuntimeExceptionを発生させれば、 間違った呼び出しを防ぐことができる. ◇ クラス定義やメソッドには必要な時にはfinalつける これは常識だが、なかなか実行されていないようである. なのでルール化しておいて、忘れないようにする. ◇ インスタンスクラスはtoStringを実装する デバッグなどに有用なので、必ず実装したい. 必要なフィールドをtoStringしてつなぎ合わせればよいので、 汎用的なヘルパーを作ることができる ◇ インスタンスクラスはequals,hashCode,Comparable,cloneを実装する. これらは不要なことも多い. 実装するというルールにしておいて「このクラスでは実装しなくてよい」 と個別判断する. ◆ メソッド ◇ メソッドの名称に否定形は使用しない 特に返値がbooleanの場合. boolean変数の名称と同じで分かりにくくなる. 分かりにくくない場合もあるので、一応個別判断が必要 ◇ Listなどの返値は値がない時はemtpy(サイズがゼロ)よりもnullを戻す emptyの場合は、データが取れた時となかった時の処理が同一にできる. これがメリット. しかし明示的に判断した方がよいとも言える. どちらにするかはシステムで統一したらよいということにしておく. 「返値が正常かどうかを判定するstaticメソッドを元のクラスに作る」を参照. ◇ 返値が正常かどうかを判定するstaticメソッドを元のクラスに作る 返値を戻したら後はどうするかは呼び出し側の勝手になる. メソッドの意図通りに解釈してくれるかは不明である. // 少しでも改善するために以下のようにする class MyClass { /** 呼び出すメソッド / public String method(String str) { // 何らかの処理 return null; // エラー発生 } /* 返値を判定するメソッド */ public static boolean errorHappened(String value) { return (value == null); } } MyClass myObj = new MyClass(); String returnValue = myObj.method("aaaa"); if (MyClass.errorHappened(returnValue)) { // エラー処理 } 「やりすぎ」という批判があるのは承知している. ◇ 返値がbooleanのメソッドは、if文に使用して馴染む名前にする class MyClass { // これでは分かりにくい public boolean check(String str) { // 何かの処理 return true; } // このようにすれば分かりやすい public boolean checkNormalEnded(String str) { // 何かの処理 return true; } } MyClass myObj = new MyClass(); // これではif文の意味がよくわからない if (myObj.check("aaaa")) { // 何らかの処理 } // こちらだと意味が分かる if (myObj.checkNormalEnded("aaaa")) { // 何らかの処理 } ◇ オブジェクト内部のList,Set,Mapなどはメソッドの戻り値にしない 必要ならばコピーを戻す 内部のListなどを戻したら勝手にいじくられる危険性がある. なのでコピーを戻す. 蛇足、ArrayListはCloneableだがListはCloneableではない ◇ 複数の返値を戻したいときにどうするか⇒クラスを作る 返値用のクラスを作ることになる. しかしそのクラスが、他で使えないようであれば、非常に悔しい. でも普通は、悔しさを我慢して、このようにする. ◇ 複数の返値を戻したいときにどうするか ⇒オブジェクト配列を戻す⇒値の取出しstaticメソッドを作成する // 前項で「どうしても嫌だ」という場合には、次のようにする class Box { public Object[] getSize() { // 最初が高さ、次が幅のつもり return new Integer[]{5,10}; } public static Integer getHeight(Object[] values) { return (Integer)values[0]; } public static Integer getWidth(Object[] values) { return (Integer)values[1]; } } Box box = new Box(); Object[] values = box.getSize(); int width = Box.getWidth(values); int height = Box.getHeight(values); ◇ 内部データが不必要なメソッドはstaticにする newする必要がない、クラス名を書くので意味が分かりやすい 常識だと思うが、時々インタンスメソッドにしているものを見かける ◇ Overloadはなるべくしない⇒ソースが分かりやすくなる 引数を確認すれば区別はつくが、面倒くさい メソッドの名称で区別ができるようにすべき ◆ ロジック ◇ 処理スピードよりも分かりやすいプログラミングを心がける 現在では当然の認識 もしスピードが必要ならば、そのようにする. ◇ ロジックが「難しい、ややこしい」と思ったら、一度は考え直す 以下のようなことを考える もっと簡単なロジックはないか? 考え方が間違ってないか? そもそも必要なロジックか? それでも必要ならば、やりぬく! ◇ まとまった処理はブロックにする 一つの処理を実現するために、複数行を記述することがある. 例えば、いくつかの変数に値を入れて、メソッドを呼び出すなど. このような時に、これらの一連の行をブロックにして、 一段インデントを下げる. ブロックにすることで、これらの行が一連のまとまった処理であることが分かる. ブロック全体にコメントを付けることができる ブロック内部に変数を定義できるので、変数の誤爆を防ぐことができる. // これはブロック全体のコメント、ブロックの中に一連の処理を記述する { int a = 0; String str = "abc"; // 以下続く. } ◇ else文では改行する // 多くの人は以下のようにif~elseを記述する int a = 1; if (a == 1) { // 必要な処理 } else { // 別の処理 } それを次のように記述する 理由はelse全体にコメントを付けることができるから 上の「まとまった処理はブロックにする」を参照. if (a == 1) { // 必要な処理 } // elseの場合の処理⇒ここがelse全体のコメント else { // 別の処理 } ◇ 三項演算子を使う これは当たり前だが、使っていないプログラムは、よく見かける. 行数が多くなり、冗長、見にくい、いらいらする. ただし入れ子の三項演算子は使用しない. // 追記、elseがないときも三項演算子を使用する → 行数が少なくなる a = (論理式) ? a = 代数式 : a; ◇ if文の内容がbreak,continue,return,throwだけであれば、 if文と同一行に記述する // 以下のようにする if (論理式) { break; } 理由は行数を少なくし、画面にできるだけ多く表示するため 他の場合は、下にロジックが流れて見にくくなるので禁止 ◇ Stringの等値判定はequalsを使用する ==で判定しているプログラムをたまに見かける. じっと見ていると貧血になりそう. しかもテストを通っていたりする.テストを通る理由は、 コピーで回していれば、等しくなるため. ◇ Stringの等値判定は定数を主語にする これはnullで落ちるのを防ぐため. ただし、きちんとnullチェックが必要な時は、前の行でチェックする必要がある String str = "bbbb"; if ("aaaa".equals((str))) { // 注、本当は"aaaa"は定数定義すべき // 一致した時の処理 } ◇ Booleanはnewしない Booleanはtrue,false(とnull)しかなく、 それはBooleanクラスに定義されているのでnewする必要はない 注、(new Boolean(true)) == (new Boolean(true))はfalseとなる. Boolean boolValue = Boolean.TRUE; ◇ nullは型を明示する 変数に代入した時はともかく、引数に渡したときは間違いが発生しやすい. 不安になって相手側のメソッドを見て確認することになる. class MyClass { public void method(String str) { // 何かの処理 } } // 以下のようにする MyClass myObj = new MyClass(); myObj.method((String)null); 本当は、nullを再定義したほうがよい. 「nullは再定義して使用する⇒クラス名+IS_NULL」を参照. ◇ if,for,whileは一文でもブロックにする 理由は、文を追加しようとしたときにバグるから. ◇ 順番を意識するループでは拡張for文を使用しない 使用しても結果は同じだが、ソースコード上「順番に処理している」 ということが見えないから. 処理に順番の番号が必要な場合は、拡張for文を使用しないのは当然だが、 例えば「入力された順番に処理する」ような場合は、 順番の番号を使わなくても拡張for文は使用しない。 ◇ 分岐(if,switch)を使わずに定数テーブルで切り分けることも考える // if文を使用する例 int a = 1; String msg; if (a == 0) { msg = "aaaa"; } else if (a == 1) { msg = "bbbb"; } // 以下続く. // 以下のようにする String[] msgs = new String[] {"aaaa","bbbb"}; msg = msgs[a]; この例はありがたみがないが、状態マシンなんかを作るときには、 このようにしないと混乱する. 注、元の値がゼロオリジンでないときは、配列を二段にしたりする必要がある 定数テーブルだけなので、短くなり、見やすい⇒バグが少なくなる ◇ elseで処理がない時は「処理なし」のコメントをしておく int a = 0; if (a == 0) { // なんらかの処理を記述 } else { // 処理なし } 当然ながらすべてのif文に、このようにはしない. 疑問が入り込む余地がある場合のみ. 「elseの処理が必要か否かを考えたうえで、不要と判断した」 ということを伝えることができる. ◇ if文の複合条件を分解して、ネストにする if文で条件がいくつもandで結びついて分かりにくい場合がある. このような時は、条件を分解してif文をネストにする. 「素人っぽい」という批判を受けても、たじろいではいけない。 バグを入れない、分かりやすくすることを重視すべき. ◇ 複合条件は括弧で分離・明示する いくつもの条件がand/orで結びついている場合は、 それぞれを()で括って分離すると見やすくなる. ◇ オブジェクトをできるだけ使いまわししない オブジェクトの生成コストがもったいないので、 使いまわしをするとバグることがある 気にしないで、新しいオブジェクトを作るほうが良い. もちろんパフォーマンス上必要な場合は使いまわす. ◇ staticメソッド呼び出しは必ずクラス名にする. 変数名にはしない.自クラスでもクラス名を書く、 staticメソッドを呼び出していることが明確になり、ソースが見やすくなる. ◇ StringBuilder(StringBuffer)はできるだけ使わない これは批判を受けるだろうが、ソースの見やすさの方が大事. 確かにパフォーマンスは違う.しかし多くの場合はそもそも絶対時間が少ない. 非常に多くのループをする場合を除いてStringBuilderは使わないようにする. 注、私の計測ではStringの追加処理で StringBuilder(StringBuffer)使用しない時は約10倍かかる. ◇ 単体テストしやすいようにメモリ上のロジックをメソッドに外だしする これも状況判断になるが、ある程度は外出しにしたほうが良い. しかし、やりすぎるとメソッド数が多くなって混乱する ◇ List,Set,Mapなどに下位オブジェクト追加するときは、 下位オブジェクトnew直後に追加する newした後に追加する処理を忘れることがあるので、 習慣づけて忘れないようにする これは分かりにくいバグとなる(こともある)ので防止するため 上記の例外はnewしたオブジェクトに値を設定していて、 条件によっては追加しないことがあるとき ◇ 例外処理ではfinalyは必ずつける、原則catchはしない catchするかしないかは、どちらの場合もあるが、しない方が多いだろう finallyは忘れないようにつける finallyの処理が不要の時も空のfinallyをつけて「処理なし」と書いておく ◆ 共通クラス ◇ 日時処理は共通クラスを作成する⇒フォーマットの統一、日時処理の統一 日時の処理は、システムのいろいろなところで、 同じような処理をしているのを見かける 同じフォーマットが、いろいろなところに記述されている. また特に日付処理を行う場合、時刻の誤差でバグが入りこむことがあるので、 防ぐ必要がある. ◇ リフレクション、通信、IOなどは共通クラスを作成する ⇒他のロジックと混在を避ける DBアクセスをDAOとして外出しにするのと同じで、 これらのような、ちょっと特殊な処理は共通クラスにする. ◇ メソッドの引数の数値の配列はprimitiveではなくwrapperにする primitiveでは、型ごとにメソッドを作成する必要がある. wrapperではNumberで統一できる.ただBigDecimalもNumber、これがやっかい ◇ クラス名やメソッド名を分かりやすくするために派生クラスを作成する 共通クラスでは、クラス名やメソッド名が、業務内容に合わない場合がある このようなときには、派生クラスを定義して、分かりやすい名称にする ただやりすぎると、クラス数が多くなる ◇ プロジェクト内の共通クラスの整備は、コストアップになるが、推進すべき プロジェクト内で共通クラスを整備するのは、むしろコストアップ要因 ⇒しかし整備すべき. 整備するコスト、メンバーが仕様を把握するコストの方が上回る. プロジェクト内での共通クラス整備は、品質の向上のためである. これを認識してないと挫けてしまう. 長い目で見れば、コストダウンにもつながってくる.
==== ended ====
今回の画像はキム・ベイシンガーの「(2004)トラブル IN ヴェガス/Elvis Has Left the Building」。