Effective Java 一人読書会(4章:クラスとインタフェース編)

| コメント(0) | トラックバック(0)

SJC-Pに追われて読むペースががた落ちです。これはいかん。

前回からの続きです。

--------------
12.クラスのメンバーへのアクセス可能性は最小限に抑える
まぁ、ですよねー的な内容。特に目新しいことはなかった。

Serializableなクラスだと、privateなメンバーでも公開APIの一部になり、変更の柔軟性が失われる。
public static finalな配列は×。配列の要素は変えたい放題なのでprivateにしてアクセサを作る。

--------------
13.Immutable(不変)クラス
-不変クラスを作るには
 1.オブジェクトを変更するメソッド(mutator)を提供しない
 2.全てのメソッドでオーバライドされないようにする
 3.全てのフィールドをprivate finalに
 4.可変オブジェクトの参照を持っていたとしても外部からアクセスされないようにする(これは上の1,2,3に含まれるよなぁ)

p62. 引数でうけとった不変オブジェクトの演算結果を不変オブジェクトで返すfunctionalなコード例

不変オブジェクトはスレッドセーフ。同期を吊る必要がない。よく使われる不変オブジェクトはキャッシュ化。例:Boolean
cloneやコピーコンストラクタは作らない。(Stringさん...)

-不変クラスのインスタンス生成コストがでかく、異なる個々のオブジェクトをあまり作りたくない場合の対処方法
 1stepで済んじゃうようなちょっとした操作。良い例かワカランが、ものすごい生成コストがでかいIntegerクラスがあったとして、そのオブジェクトにnを加算させたいみたいな場合→加算させるmutatorを作っちゃう。ただし不変で無くなる。

 1stepで済まないような操作→(複数stepを1メソッドにまとめて)可変コンパニオンクラスを使用。
 例:BigIntegerのdivide() 内部でMutableBigIntegerを使用。不変クラスの中で可変オブジェクトを低コストで生成して、これらをいじって演算結果を返している。

ソース見た方が早いかも。MutableBigIntegerなんてあったんだね。パッケージプライベートだからapiには出てないのか。
コンパニオンクラスなんて表現は初めて知ったのだが、これは不変クラスと一緒に使う補助的なクラスと考えればよいのか?

 そもそもどんな操作をされるのかワカラン場合→publicなコンパニオンクラスを提供。例:StringBuffer

2.全てのメソッドでオーバライドされないようにする の捕捉
全てのメソッドがオーバライドされないようにする為には...
classをfinal指定 or コンストラクタをprivate, コレの代わりに項目1で取り上げたstatic factory mehod。
柔軟性の面からEffective Javaでは後者の方が良いだろうと記述している。

-可変クラスのとりうる状態は少ない方が分かり易い
 →再初期化メソッドは作らない方がイイ?パフォーマンスは?
  →複雑さが増す方が厄介だし、再初期化メソッドによるパフォーマンス改善はあまりない。


---
ノートにメモってのは13章までか。現在18章辺りを読んでるが、はやくも14章あたりのことを忘れてるので明日からまた14章読み直して気になった部分をメモるかな。

続き。

--------------
14.extendsよりもコンポジションによる拡張
まぁ、これも12章同様、既に知っていたのですんなりと読めたかな。

p69-72
既存のコレクションクラスをextendsして無理矢理拡張したオレオレHashSetをコンポジション(いわゆるGoF23パターンの1つであるデコレータパターン)に書き換えた例の紹介。

extendsによる拡張は脆い。
 →親が自己利用(self-use)していると親クラス内部の実装に依存するため、親クラスの細かい修正でも子に影響が出る。
自己利用の例:本ではHashSetのaddAll()を挙げている。addAllは親クラスのAbstractCollectionで定義されており、その中でadd()を使っている。

コンポジションによる拡張は?
 →外部公開APIを使っているのでそうそう振る舞いは変わらないはず。

この項目で知らなかったのは委譲と転送の違いだ。
どうやら今まで自分が委譲だと思っていたものは転送と呼ぶらしい
参考:What is (Not) Delegation
(consultationで転送?Effective Java原文ではなんと書いてあるんだろう?)

委譲の場合、ラップする側の情報をラップされる側が把握できないとダメらしい。
だからラップされる側にラップする側のthis/selfを渡して初めて委譲と呼ぶことになるそうな。調べてみると更に真の委譲(true delegation)と呼ばれるモノがあり、これはラップされた側のthis/selfがラップしている側を参照している場合のことらしい。だから上のリンク先の図で表現してるのはtrue delegationの方だね。Javaのthisは常にそのクラスのオブジェクトへの参照なので、真の委譲は表現できないって事になる。ほへー( ´p`)

--------------
15.継承される事を意識した設計、ドキュメント作り。でなきゃ継承できないようにしてしまへ
項目14の続きみたいな感じかな?項目14にあったようなオーバライド可能メソッドの自己利用はちゃんとドキュメント化する。どこどこのメソッドでコイツを呼んでます。みたいな。
本ではドキュメントの良い例としてAbstractCollectionのremove()を挙げている。

継承される事を意識した設計
 →protectedでフックメソッドを提供。
GoFで言うところのTemplate Methodパターンだね。

 →項目10のcloneと被る内容だけど、コンストラクタ内でオーバライド可能なメソッド呼び出しはしない。もしオーバライドされてしまったら、子クラスのメソッドの方に遷移してしまうため。子クラスのインスタンスが出来てない状態で子クラスの(オーバライドされた)インスタンスメソッドが呼ばれると変な挙動をする可能性が大。

一般的なの具象クラスは?
 →どこでオーバライドされるか分からないからfinal class指定か、お馴染みのコンストラクタをprivate&static factory methodの提供。

--------------
16.継承クラスよりインタフェース
インターフェースを用いた多重継承について言及している。
自分の中ではクラスを継承するやり方はカテゴリー分け、振る舞いをインターフェースにより追加するやり方はタグ付けに近いというイメージかな。使い勝手の良さからインタフェースの方が良いと述べている。

インターフェースと抽象クラスをうまく組み合わせている例:AbstractInterfaseclass
→骨格を実装。最終的なインタフェースの実装社にとって便利なクラス。コレクション系クラスにいっぱいある。

抽象クラスの方がインタフェースよりより優れている点→メソッド増やすのが楽。逆にインタフェースでは一度外部公開したらもう後戻りは出来ない。インタフェースの設計は慎重に。

--------------
17.定数のみのインタフェースは×
クラス内で定数かenumか、又はpublic static finalにしてutilクラスに移す。

定数の為だけにimplementsを増やすのはデメリットしかない。
 -広く公開している場合、一回implementsしたら(バイナリ互換の為)もう容易には変えられない。
 -実装の詳細が外部公開APIとして漏れる(例:javadoc等)

--------------
18.ネストクラス
SJC-Pの勉強でもネストクラス辺りは難しかったなぁと思いつつ、読み進める。

+-----------------+
| クラス          |→このクラスのインスタンスをエンクロージングインスタンスと呼ぶらしい。
|   +----------+  |
|   |ネストクラス |  |
|   +----------+  |
+-----------------+

ネストクラスは4種類ある。
 1.非staticメンバークラス
 2.staticメンバークラス
 3.無名クラス
 4.ローカルクラス

外側のクラスの補助クラスとして使いたいが、限定されたメソッドの外からも参照したいし、メソッド内に定義するには長すぎる場合→メンバークラスに。

エンクロージングインスタンスにネストクラスがアクセスする必要がない場合→staticなメンバークラスに。(ネストクラスの個々のオブジェクトがエンクロージングインスタンスを参照しないようにメモリを節約する効果がある。)

クラスを定義しても、そのクラスの利用箇所が1つのメソッドに限定、クラスの内容も短いし、名前も付けづらい場合→無名クラス

名前は付けやすい場合→ローカルクラス(でも使うの?)

p89.Comparatorを実装した無名クラスのコード例。
自分が見た感じだとソート回数が限られてるのなら毎回無名クラスをnewするのもアリだが、頻繁にソートするのならばcomparator相当のクラスはSingletonにして使い回すのがベターだろう。(これについては項目22で言及されている)

でもそーすると無名クラスにしてはでかいクラスになってしまう。
-クロージャ使えたっけ?→JDK7かららしい
-クロージャはPerlでいうところの無名サブルーチン。でもJavaだとクラスになってしまうのでちょっとおおげさ。

---
4章はだいたいこんな感じか。次章に続きます。

トラックバック(0)

トラックバックURL: https://hoge.sub.jp/blog-cgi/mt/mt-tb.cgi/1563

このブログ記事について

このページは、Lyoが2008年5月 6日 22:56に書いたブログ記事です。

ひとつ前のブログ記事は「タケノコの会とNintendo DS」です。

次のブログ記事は「たまにはC言語の話題」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

月別 アーカイブ

OpenID対応しています OpenIDについて
Powered by Movable Type 7.9.3