Effective java 第二版輪読会資料 第4章 項目16,21
-
Upload
takeshi-yoshimura -
Category
Technology
-
view
203 -
download
0
description
Transcript of Effective java 第二版輪読会資料 第4章 項目16,21
Effective Java 第二版輪読会資料
第4章 クラスとインタフェース
1
自己紹介
• 吉村 武志
• Twitter:@takesi_yosimura
• 仕事:Classic ASP書いてます 以前はJava書いていて一応7~8年くらい?
2
第4章の項目
3
• 項目13 クラスとメンバーへのアクセス可能性を最小限にする
• 項目14 publicのクラスではpublicのフィールドではなく、アクセッサーメソッドを使う
• 項目15 可変性を最小限にする
• 項目16 継承よりコンポジションを選ぶ
• 項目17 継承のために設計および文書化する、でなければ継承を禁止する
• 項目18 抽象クラスよりインタフェースを選ぶ
• 項目19 型を定義するためだけにインタフェースを使用する
• 項目20 タグ付クラスよりクラス階層を選ぶ
• 項目21 戦略を表現するために関数オブジェクトを使用する
• 項目22 非staticのメンバークラスよりstaticのメンバークラスを選ぶ
4
10個!
• 項目13 クラスとメンバーへのアクセス可能性を最小限にする
• 項目14 publicのクラスではpublicのフィールドではなく、アクセッサーメソッドを使う
• 項目15 可変性を最小限にする
•項目16 継承よりコンポジションを選ぶ • 項目17 継承のために設計および文書化する、でなければ継承を禁止する
• 項目18 抽象クラスよりインタフェースを選ぶ
• 項目19 型を定義するためだけにインタフェースを使用する
• 項目20 タグ付クラスよりクラス階層を選ぶ
• 項目21 戦略を表現するために関数オブジェクトを使用する • 項目22 非staticのメンバークラスよりstaticのメンバークラスを選ぶ
5
2つだけやります
項目16 継承よりコンポジションを選ぶ
6
項目16 継承よりコンポジションを選ぶ
• 継承はカプセル化を破ってしまうため問題がある
• 継承の代わりにコンポジションと転送を使え
• 継承が適切なのは「is-a」関係だけ
7
まとめ
サブクラスがスーパークラスの実装に依存
先のInstrumentedHashSetはHashSetを継承して、add、addAllでの追加数をカウントするように実装している。
が、実際に3個の要素を追加するようにaddAllを呼び出すと、追加数は6になっている。
HashSetのaddAllが内部でaddを呼んでいるため、二重にカウントされてしまう。
9
未だ問題あり先の例のように修正すると、カウントだけは正しくなる。
が、この修正によってHashSetのaddAllはもはや呼び出されなくなる。
また、こういった修正は難しくて間違いやすい上、出来ないケースもある。
11
バージョンアップでNG• スーパークラスのメソッドが増えた場合、先ほどの例でもしHashSetの要素追加メソッドが増えると、サブクラスでは追加回数のカウント漏れが出る事になるかもしれない。
• サブクラスでスーパークラスに無いメソッドを追加していた場合、スーパークラスのバージョンアップ時に増えたメソッドのシグネチャが、サブクラスの追加メソッドと衝突する不幸があると困る。
12
解決のために• compositionを使おう。
• 既存クラスのインスタンスを参照するprivateのフィールドを新たなクラスに持たせて、転送メソッドを実装しよう。
• 確認http://ideone.com/zkBQ4O
13
compositionについて• 先の例のInstrumentedHashSetは ラッパークラス。
• Decoratorパターン。
• 委譲(Delegation)に見えるが違う。
• コールバックフレームワークで使うのは不向き。
14
継承が適切なとき
• 「is-a」関係が成り立つときだけ
• 「すべてのBは本当にAであるか?」
15
例えば?
• 「すべてのとんこつラーメンは、ラーメンの一種である」
• 「すべてのラーメンセットは、ラーメンの一種で………?」
16
項目16 継承よりコンポジションを選ぶ
• 継承はカプセル化を破ってしまうため問題がある
• 継承の代わりにコンポジションと転送を使え
• 継承が適切なのは「is-a」関係だけ
17
まとめ
項目21 戦略を表現するために 関数オブジェクトを使用する
18
項目21 戦略を表現するために 関数オブジェクトを使用する
• 具象戦略(concreate strategy)クラスの使用
• 複数箇所で使うなら、static finalフィールドに入れる事も検討。
• Java8からはラムダ式でよさそう
19
まとめ
戦略パターン (Strategyパターン)
• 言語によっては以下がある
• 関数ポインタ(function pointer)
• 委譲(delegate)
• ラムダ式(lambda expression)
• 呼び出す関数の振る舞いを、なんとかする
20
具体例class StringLengthComparator {
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
}
• 文字列比較に対する具象戦略(concrete strategy)クラス
• 状態が無い(stateless)
21
戦略インタフェースpublic interface Comarator<T> {
public int compare(T t1, T t2);
}
• メソッドにconcrete strategyインスタンスを渡すために、パラメータに関する適切な型が必要
• 具体的なクラスでなく、インタフェースを定義する22
戦略インタフェースを実装 した具象戦略クラス
class StringLengthComparator implements Comparator<String> {
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
}
• クラス本体は変更無し23
具象戦略クラスは多くの場合 無名クラスで実装した
Arrays.sort(stringArray, new Comparator<String>() {
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
});
• 呼び出し時毎にインスタンスを生成する事に注意
• 繰り返し呼ぶならば、保存して再利用を検討http://ideone.com/SKgJPV
24
Java8からはラムダ式でArrays.sort(stringArray, new Comparator<String>() {
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
});
↓
Arrays.sort(array, (String s1, String s2) -> s1.length() - s2.length());
• なお、ラムダ式は無名クラスのシンタックスシュガーではない
25
ラムダ式は staticメソッド参照と同値
public static void main(String[] args) {
String[] array = {"aaaaa", "bb", "cccc", "d", "eee"};
Arrays.sort(array, (String s1, String s2) -> s1.length() - s2.length());
}
と同じなのは、
public static int lambda$0(String s1, String s2) { return s1.length() - s2.length();}
public static void main(String[] args) {
String[] array = {"aaaaa", "bb", "cccc", "d", "eee"};
Arrays.sort(array, Hogehoge::lambda$0);
}
26
ラムダ式、詳しくは• きしださんありがとうございます
• Java8 Lambdaの文法拡張まとめ - きしだのはてな http://d.hatena.ne.jp/nowokay/20130824
• Java8のlambda構文がどのようにクロージャーではないか - きしだのはてな http://d.hatena.ne.jp/nowokay/20130522
27
項目21 戦略を表現するために 関数オブジェクトを使用する
• 具象戦略(concreate strategy)クラスの使用
• 複数箇所で使うなら、static finalフィールドに入れる事も検討。
• Java8からはラムダ式でよさそう
28
まとめ