Effective java 第二版輪読会資料 第4章 項目16,21

28
Effective Java 第二版 輪読会資料 第4章 クラスとインタフェース 1

description

 

Transcript of Effective java 第二版輪読会資料 第4章 項目16,21

Page 1: Effective java 第二版輪読会資料 第4章 項目16,21

Effective Java 第二版輪読会資料

第4章 クラスとインタフェース

1

Page 2: Effective java 第二版輪読会資料 第4章 項目16,21

自己紹介

• 吉村 武志

• Twitter:@takesi_yosimura

• 仕事:Classic ASP書いてます 以前はJava書いていて一応7~8年くらい?

2

Page 3: Effective java 第二版輪読会資料 第4章 項目16,21

第4章の項目

3

Page 4: Effective java 第二版輪読会資料 第4章 項目16,21

• 項目13 クラスとメンバーへのアクセス可能性を最小限にする

• 項目14 publicのクラスではpublicのフィールドではなく、アクセッサーメソッドを使う

• 項目15 可変性を最小限にする

• 項目16 継承よりコンポジションを選ぶ

• 項目17 継承のために設計および文書化する、でなければ継承を禁止する

• 項目18 抽象クラスよりインタフェースを選ぶ

• 項目19 型を定義するためだけにインタフェースを使用する

• 項目20 タグ付クラスよりクラス階層を選ぶ

• 項目21 戦略を表現するために関数オブジェクトを使用する

• 項目22 非staticのメンバークラスよりstaticのメンバークラスを選ぶ

4

10個!

Page 5: Effective java 第二版輪読会資料 第4章 項目16,21

• 項目13 クラスとメンバーへのアクセス可能性を最小限にする

• 項目14 publicのクラスではpublicのフィールドではなく、アクセッサーメソッドを使う

• 項目15 可変性を最小限にする

•項目16 継承よりコンポジションを選ぶ • 項目17 継承のために設計および文書化する、でなければ継承を禁止する

• 項目18 抽象クラスよりインタフェースを選ぶ

• 項目19 型を定義するためだけにインタフェースを使用する

• 項目20 タグ付クラスよりクラス階層を選ぶ

• 項目21 戦略を表現するために関数オブジェクトを使用する • 項目22 非staticのメンバークラスよりstaticのメンバークラスを選ぶ

5

2つだけやります

Page 6: Effective java 第二版輪読会資料 第4章 項目16,21

項目16 継承よりコンポジションを選ぶ

6

Page 7: Effective java 第二版輪読会資料 第4章 項目16,21

項目16 継承よりコンポジションを選ぶ

• 継承はカプセル化を破ってしまうため問題がある

• 継承の代わりにコンポジションと転送を使え

• 継承が適切なのは「is-a」関係だけ

7

まとめ

Page 8: Effective java 第二版輪読会資料 第4章 項目16,21

カプセル化を破るとは?

• サブクラスはスーパークラスの実装に依存する

• 実際に確認。http://ideone.com/x0vC9S

8

Page 9: Effective java 第二版輪読会資料 第4章 項目16,21

サブクラスがスーパークラスの実装に依存

先のInstrumentedHashSetはHashSetを継承して、add、addAllでの追加数をカウントするように実装している。

が、実際に3個の要素を追加するようにaddAllを呼び出すと、追加数は6になっている。

HashSetのaddAllが内部でaddを呼んでいるため、二重にカウントされてしまう。

9

Page 10: Effective java 第二版輪読会資料 第4章 項目16,21

修正を試みる

• 修正例確認。http://ideone.com/40sVC6

10

Page 11: Effective java 第二版輪読会資料 第4章 項目16,21

未だ問題あり先の例のように修正すると、カウントだけは正しくなる。

が、この修正によってHashSetのaddAllはもはや呼び出されなくなる。

また、こういった修正は難しくて間違いやすい上、出来ないケースもある。

11

Page 12: Effective java 第二版輪読会資料 第4章 項目16,21

バージョンアップでNG• スーパークラスのメソッドが増えた場合、先ほどの例でもしHashSetの要素追加メソッドが増えると、サブクラスでは追加回数のカウント漏れが出る事になるかもしれない。

• サブクラスでスーパークラスに無いメソッドを追加していた場合、スーパークラスのバージョンアップ時に増えたメソッドのシグネチャが、サブクラスの追加メソッドと衝突する不幸があると困る。

12

Page 13: Effective java 第二版輪読会資料 第4章 項目16,21

解決のために• compositionを使おう。

• 既存クラスのインスタンスを参照するprivateのフィールドを新たなクラスに持たせて、転送メソッドを実装しよう。

• 確認http://ideone.com/zkBQ4O

13

Page 14: Effective java 第二版輪読会資料 第4章 項目16,21

compositionについて• 先の例のInstrumentedHashSetは ラッパークラス。

• Decoratorパターン。

• 委譲(Delegation)に見えるが違う。

• コールバックフレームワークで使うのは不向き。

14

Page 15: Effective java 第二版輪読会資料 第4章 項目16,21

継承が適切なとき

• 「is-a」関係が成り立つときだけ

• 「すべてのBは本当にAであるか?」

15

Page 16: Effective java 第二版輪読会資料 第4章 項目16,21

例えば?

• 「すべてのとんこつラーメンは、ラーメンの一種である」

• 「すべてのラーメンセットは、ラーメンの一種で………?」

16

Page 17: Effective java 第二版輪読会資料 第4章 項目16,21

項目16 継承よりコンポジションを選ぶ

• 継承はカプセル化を破ってしまうため問題がある

• 継承の代わりにコンポジションと転送を使え

• 継承が適切なのは「is-a」関係だけ

17

まとめ

Page 18: Effective java 第二版輪読会資料 第4章 項目16,21

項目21 戦略を表現するために 関数オブジェクトを使用する

18

Page 19: Effective java 第二版輪読会資料 第4章 項目16,21

項目21 戦略を表現するために 関数オブジェクトを使用する

• 具象戦略(concreate strategy)クラスの使用

• 複数箇所で使うなら、static finalフィールドに入れる事も検討。

• Java8からはラムダ式でよさそう

19

まとめ

Page 20: Effective java 第二版輪読会資料 第4章 項目16,21

戦略パターン (Strategyパターン)

• 言語によっては以下がある

• 関数ポインタ(function pointer)

• 委譲(delegate)

• ラムダ式(lambda expression)

• 呼び出す関数の振る舞いを、なんとかする

20

Page 21: Effective java 第二版輪読会資料 第4章 項目16,21

具体例class StringLengthComparator {

public int compare(String s1, String s2) {

return s1.length() - s2.length();

}

}

• 文字列比較に対する具象戦略(concrete strategy)クラス

• 状態が無い(stateless)

21

Page 22: Effective java 第二版輪読会資料 第4章 項目16,21

戦略インタフェースpublic interface Comarator<T> {

public int compare(T t1, T t2);

}

• メソッドにconcrete strategyインスタンスを渡すために、パラメータに関する適切な型が必要

• 具体的なクラスでなく、インタフェースを定義する22

Page 23: Effective java 第二版輪読会資料 第4章 項目16,21

戦略インタフェースを実装 した具象戦略クラス

class StringLengthComparator implements Comparator<String> {

public int compare(String s1, String s2) {

return s1.length() - s2.length();

}

}

• クラス本体は変更無し23

Page 24: Effective java 第二版輪読会資料 第4章 項目16,21

具象戦略クラスは多くの場合 無名クラスで実装した

Arrays.sort(stringArray, new Comparator<String>() {

public int compare(String s1, String s2) {

return s1.length() - s2.length();

}

});

• 呼び出し時毎にインスタンスを生成する事に注意

• 繰り返し呼ぶならば、保存して再利用を検討http://ideone.com/SKgJPV

24

Page 25: Effective java 第二版輪読会資料 第4章 項目16,21

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

Page 26: Effective java 第二版輪読会資料 第4章 項目16,21

ラムダ式は 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

Page 27: Effective java 第二版輪読会資料 第4章 項目16,21

ラムダ式、詳しくは• きしださんありがとうございます

• Java8 Lambdaの文法拡張まとめ - きしだのはてな http://d.hatena.ne.jp/nowokay/20130824

• Java8のlambda構文がどのようにクロージャーではないか - きしだのはてな http://d.hatena.ne.jp/nowokay/20130522

27

Page 28: Effective java 第二版輪読会資料 第4章 項目16,21

項目21 戦略を表現するために 関数オブジェクトを使用する

• 具象戦略(concreate strategy)クラスの使用

• 複数箇所で使うなら、static finalフィールドに入れる事も検討。

• Java8からはラムダ式でよさそう

28

まとめ