アウトプット量の方が少ないな...どーもいかん。
前回からの続き。
--------------
23.パラメータの正当性検査
(本当は無い方がイイのだが...)メソッドに渡す制約がある場合、その制約についての説明をドキュメント化する&制約違反を起こしていないかチェックする。(もし変なパラメータが渡ってきたらさっさと例外を投げる。)
まぁ当たり前の話だよね。
変なパラメータを受け取ったときに投げつける例外の例として本では3つの例外を挙げている。
-IlligalArgumentExceprion
-NullPointerExceprion
-indexOutOfBoundsExceprion
個人的にはどうも下2つには納得行かないのだが...。わざわざプログラマーが外に投げるべき例外なのかな?
[追記]
よくよく考えたら3つとも実行時例外だから大差なかった。
オレの作ったライブラリーが悪いんじゃなくて、利用してるお前の使い方が変なんだよと意思表示したいときは投げてもいいのかもなぁ。
他にもこれはどうなんだろうと思うところがあって、例えばprivateなメソッドに渡すパラメータのチェックはassert使うことを推奨しているのだが、assert使うにはVMに引数-ea必要だし見落としがちにならんかなぁ?と個人的に思う。
privateなメソッドに対してはtestを書いた方が良いんじゃないかねぇ?
メソッドで生成したデータが次のメソッドに投げられ、そこで使われるような場合は特にちゃんと(作られた時点で)検査すべき。データを作った側か、使う側に問題があるのかハッキリする。
検査のコストがでかい場合は検査を諦めるという選択肢も考慮に入れる(まぁ、当たり前か)
検査の途中で発生してしまった例外をさっき紹介した例外に置き換えたい→例外翻訳イデオム(項目43)の導入を検討する。
--------------
24.防御的コピー
外部から可変オブジェクトへの参照をもらって内部変数にsetするだけでは脆い。
(外部にある参照から内部を書き換えられる可能性がある。ちょうど昨日書いた"HttpSessionの実装について"の冒頭がコレに当たる)
可変なパラメータを渡される前にコピーを取ってから受け取る。(その際に妥当性チェックが必要ならコピーに対して行う)
そして、渡すときにもコピーを取ってから渡すようにすれば外部から内部をいじれなくなる。
パラメータとして渡されるオブジェクトがオーバライド可能なクラスなら...→cloneは使わずにコンストラクタを使ってコピーを作る。 cloneを使うと生成したオブジェクトを全てリストに保持しちゃうような悪意に満ちたサブクラスのオブジェクトを渡されたときに困る。(でもコレはいく らなんでも考えすぎなような気もするが...まぁ、ここまで想定できるようになれば完璧なんだろうが)
不変クラスだけでなく、可変クラスでも外部から勝手に内部オブジェクトを弄られると困るような場合もこの辺のことは頭に入れておくべき。
可変オブジェクトを構成するプリミティブ型データだけを持つという手も有効。多分こっちの方がコストも抑えられて楽。(例:Dateのオブジェクトを持つのではなく、DateのgetTime()で返されるlong値のみを持つ...とか。)
--
とりあえず今日はここまで。
続き。
--------------
25.メソッドの設計について
-メソッド名は適切に。
SunのJavaコーディング規約
deleteとremoveどっちがいい?→選択する単語については"The Java Developers Almanac"を読めってことらしい。だいたいの傾向が分かる。ちなみにこの場合はdeleteを選択するべき。
-1つのクラスに便利なメソッドを集中させない→使う側が迷う。
-メソッドの引数は多くても3つまで。同じ型のパラメータが続くのは特に酷い。
→んじゃどうするのか?
1.複数のメソッドに分ける方法(ここ、すげぇ読みにくかった...)
例.Listインタフェース
本では
このインタフェースは、サブリスト内の要素の最初あるいは最後のインデックスを見つけるメソッドを提供していません。
とあるが、ここで言ってるメソッドとは正しくは指定した要素がサブリスト内の最初(あるいは最後)に現れる位置のインデックスを返すメソッドのこと。
最初この文章を見たとき、なんで3つ引数が居るのかワケ分からなかったけど、こう解釈すればlist.indexOfSubList(obj, fromIndex, lastIndex);となるだろう。
このようなメソッドを作るのではなく、subList()とindexOf()を組み合わせて使うようなapiにしろと言っているようだ。こうすれば引数の数が2,1に分かれ、しかも使い勝手がいい。
2.パラメータをまとめるstaticなメンバークラスを作る。
-パラメータの型は具象クラスよりもインタフェースを選ぶ。
→インタフェースを実装する他のクラスのインスタンスも渡せるようになる。
-関数オブジェクト
StrategyやVisitorパターンのような使い方にとどめる。関数オブジェクトをメソッドからメソッドに渡すような、駆使しまくりな使い方はしない。lispでやれってことですね。
--------------
26.オーバロードとオーバライド
どちらも多態性関連の用語だけど、メソッドの選択は
オーバライド:実行時に決定
オーバロード:コンパイル時に決定(静的)
といった違いがある。同じパラメータ数のオーバロードされたメソッドは極力避ける。作ったとしても型が根本的に異なる(つまり親子関係がない)ようにする。
--------------
27.nullを返すメソッド
nullを返すと利用者側にnullチェックを強制してしまう。これは色々とメンドイ。ifの条件式が汚くなりがち。
例えば、配列を返すときに配列がカラだった場合、nullではなく長さ0の配列を返すようにする。
別の例:collectionのtoArray(T[])
引数から受け取った配列を利用。
Null Objectパターンもこの辺の話と関連あるね。
--------------
28.javadocコメントの書き方
公開APIにはドキュメントを必ず書く。前の項目と被るけど、パラメータの制約やメソッド実行による副作用があるならちゃんと書くべき。
ドキュメントに式を書いたときは&や不等号に注意する。(ちゃんとHTMLのエスケープシーケンスで書いてるか?)
これ、完全に守ってなかったな...でもイチイチ特殊文字をエスケープシーケンスで書いてられるか!ってところ。もう少しjavadocが賢ければいいのに。試してないけど「AnyEdit」ってプラグインがあった。
-英語ドキュメントの話
インスタンスメソッドの説明でthisが出てきたらそれはメソッドが呼び出されたオブジェクトのこと。
ドキュメントコメントの最初では概要を説明する部分。ピリオドを使うとそこで途切れるので注意する。HTMLシーケンスでピリオドを打って解決という手もある。
メソッドコメントの継承も可。(ただし、コメントをそのまま丸々使う場合のみ。部分的な修正は出来ない。)
複数のメソッドの相互作用によってプログラムが機能している場合
→全体のアーキテクチャを説明する外部ドキュメントを別に作り、その外部ドキュメントへのリンクをjavadocコメントに書く。