home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

Java/「Javaデザインパターン徹底攻略」読書メモ (v1)

Java/「Javaデザインパターン徹底攻略」読書メモ (v1)

Java / 「Javaデザインパターン徹底攻略」読書メモ (v1)
id: 826 所有者: msakamoto-sf    作成日: 2010-11-05 14:44:54
カテゴリ: Java 読書 

2009年10月頃購入して、一年間本棚で埃を被っていたが、ようやく読み終えることが出来ました。

大学卒業後これまでの6年間、デザインパターンについてはWeb記事だったり雑誌記事のつまみ食いばかりでした。書籍として体系立てられたものを読んだことが無かったので、今までずっと独学・我流ならではの不安感があったわけです。手頃な一冊が無いかと探し、結城氏の本は分厚かったので、薄めのこちらを購入してみました。

三行レビュー:

  • 程よくコンパクトでカタログリファレンスとして手元に置いておきたい。
  • 「薬も過ぎれば毒」と、おそらく実際の開発経験に基づいて誠実に説いてくれている点が素晴らしい。
  • サンプルが、シンプル過ぎて逆に分かりづらいかも。

Amazonレビューでも概ね好評、また、若干のマイナス評価についてはやはり例題が簡単すぎる点が挙げられていました。自分の読後感も的はずれでは無かったようです。

GOFの23パターンがコンパクトかつスマートにまとめられているのは、マイナス評価レビューを含めても皆さん認められているようです。難点はやはり例題が簡単すぎて却って分かりづらくないか?というところなので、その辺りをWebや雑誌記事、あるいは実際の開発現場でベテランと相談したりしつつ学んでいくと効果的だと思われます。極論を言ってしまえば、実際に使ってみて、メリットや使いすぎたときのデメリットを自分で体験してみるのが一番効果的な学習方法でしょう。

なお、本書ではデザインパターンを特に分類していません。本家での分類については適宜Wikipediaなどを参照していただくとして、読書メモとして自分なりに分類してみたのを書いておきます。

オブジェクト生成の隠蔽・抽象化・省力化

コンストラクタを直接呼ばせず、一段挟むことでわかりやすい設計にするテクニックです。

  • 01 . Factory Method
  • 02 . Abstract Factory
  • 03 . Builder
  • 04 . Prototype
  • 05 . Singleton
  • 11 . Flyweight
既存のオブジェクトをラップして拡張・委譲・中継

既存クラスを拡張したいときに、それを包み込むクラスを用意することで既存クラスを直接修正しなくて済むようにするテクニックです。

  • 06 . Adapter
  • 07 . Bridge
  • 09 . Decorator
  • 12 . Proxy
数珠繋ぎに再帰や委譲処理

ツリー構造やコンテナ内の要素を順繰りに辿っていって何かさせたい時のテクニックです。再帰処理や入れ子構造を使いますので、直感的には分かりづらいかもしれません。

  • 08 . Composite
  • 13 . Chain of Responsibility
  • 15 . Interpreter
  • 23 . Visitor
複数のオブジェクトやメソッド呼び出しパターンをカプセル化

そのまんまです。

  • 10 . Facade
  • 14 . Command
オブジェクトの上手な組み合わせ方、交通整理

オブジェクト間のやりとりを上手く整理するテクニックや、使用場面がかなり絞られるテクニック(Memento, State)などです。

  • 16 . Iterator
  • 17 . Mediator
  • 18 . Memento
  • 19 . Observer
  • 20 . Sate
  • 21 . Strategy
  • 22 . Template Method

最後におまけとして、 "08 . Composite" と "23 . Visitor" を少し弄ったものを載せておきます。


まず、 "08 . Composite" ですが本書の例題では複数のNodeを保持させていません。
ということで、Nodeを複数保持するバージョンを作ってみました。
より実際のツリー構造を操作するイメージに近いと思います。

MyMain.java:

interface MyNode {
    void viewNames();
}
 
class MyComposite implements MyNode {
    private String name;
    java.util.List<MyNode> nodes = 
        new java.util.ArrayList<MyNode>();
 
    MyComposite(String name) {
        this.name = name;
    }
 
    void addNode(MyNode n) {
        nodes.add(n);
    }
 
    public void viewNames() {
        System.out.println("Composite : " + name);
        java.util.Iterator<MyNode> it = nodes.iterator();
        while (it.hasNext()) {
            MyNode n = it.next();
            n.viewNames();
        }
    }
}
 
class MyLeaf implements MyNode {
    private String name;
 
    MyLeaf(String name) {
        this.name = name;
    }
 
    public void viewNames() {
        System.out.println("Leaf : " + name);
    }
}
 
public class MyMain {
    public static void main(String args[]) {
        MyComposite r = new MyComposite("root");
        MyComposite d1 = new MyComposite("d1");
        MyComposite d2 = new MyComposite("d2");
        MyComposite d3 = new MyComposite("d3");
        MyLeaf lr1 = new MyLeaf("lr1");
        MyLeaf lr2 = new MyLeaf("lr2");
        r.addNode(d1);
        r.addNode(d2);
        r.addNode(d3);
        r.addNode(lr1);
        r.addNode(lr2);
 
        MyComposite d1A = new MyComposite("d1A");
        MyComposite d1B = new MyComposite("d1B");
        MyComposite d1C = new MyComposite("d1C");
        MyLeaf ld1A = new MyLeaf("ld1A");
        MyLeaf ld1B = new MyLeaf("ld1B");
        MyLeaf ld1C = new MyLeaf("ld1C");
        d1.addNode(d1A);
        d1.addNode(d1B);
        d1.addNode(d1C);
        d1.addNode(ld1A);
        d1.addNode(ld1B);
        d1.addNode(ld1C);
 
        MyLeaf ld1Aa = new MyLeaf("ld1Aa");
        d1A.addNode(ld1Aa);
 
        MyComposite d1Ba = new MyComposite("d1Ba");
        MyComposite d1Bb = new MyComposite("d1Bb");
        MyLeaf ld1Ba = new MyLeaf("ld1Ba");
        d1B.addNode(d1Ba);
        d1B.addNode(d1Bb);
        d1B.addNode(ld1Ba);
 
        MyComposite d3A = new MyComposite("d3A");
        MyLeaf ld3A = new MyLeaf("ld3A");
        d3.addNode(d3A);
        d3.addNode(ld3A);
 
        r.viewNames();
    }
}

コンパイル+実行

> javac MyMain.java
> java MyMain
Composite : root
Composite : d1
Composite : d1A
Leaf : ld1Aa
Composite : d1B
Composite : d1Ba
Composite : d1Bb
Leaf : ld1Ba
Composite : d1C
Leaf : ld1A
Leaf : ld1B
Leaf : ld1C
Composite : d2
Composite : d3
Composite : d3A
Leaf : ld3A
Leaf : lr1
Leaf : lr2

ノードの枝と葉を辿っていく様子がよりリアルに伝わってきます。
・・・伝わってきますよね?

とはいえ、表示がちょっと寂しいというか分かりづらいです。階層構造が表現できていません。
そこで "23 . Visitor" パターンを組み合わせてみます。「自分は今、何階層目を表示しているのだろう?」という情報をVisitorに持たせてみましょう。各ノードは、自分の居る階層の具体的な数字は知らなくてもオッケーです。「Visitorが持ってる階層の数字+1が、自分の直下のノード階層」というわけです。

というわけで、再度MyMain.java:

interface MyNode {
    String getDisplayName();
    void visit(MyVisitor v, int level);
}
 
class MyVisitor {
    void printNode(MyNode n, int level) {
        String l = "";
        for (int i = 0; i < level; i++) {
            l += ">";
        }
        System.out.println(l + " " + n.getDisplayName());
    }
}
 
class MyComposite implements MyNode {
    private String name;
    java.util.List<MyNode> nodes = 
        new java.util.ArrayList<MyNode>();
 
    MyComposite(String name) {
        this.name = name;
    }
 
    void addNode(MyNode n) {
        nodes.add(n);
    }
 
    public String getDisplayName() { return "Composite : " + name; }
 
    public void visit(MyVisitor v, int level) {
        v.printNode(this, level);
        level++;
        java.util.Iterator<MyNode> it = nodes.iterator();
        while (it.hasNext()) {
            MyNode n = it.next();
            n.visit(v, level);
        }
    }
}
 
class MyLeaf implements MyNode {
    private String name;
 
    MyLeaf(String name) {
        this.name = name;
    }
 
    public String getDisplayName() { return "Leaf : " + name; }
 
    public void visit(MyVisitor v, int level) {
        v.printNode(this, level);
    }
}
 
public class MyMain {
    public static void main(String args[]) {
        MyComposite r = new MyComposite("root");
        MyComposite d1 = new MyComposite("d1");
        MyComposite d2 = new MyComposite("d2");
        MyComposite d3 = new MyComposite("d3");
        MyLeaf lr1 = new MyLeaf("lr1");
        MyLeaf lr2 = new MyLeaf("lr2");
        r.addNode(d1);
        r.addNode(d2);
        r.addNode(d3);
        r.addNode(lr1);
        r.addNode(lr2);
 
        MyComposite d1A = new MyComposite("d1A");
        MyComposite d1B = new MyComposite("d1B");
        MyComposite d1C = new MyComposite("d1C");
        MyLeaf ld1A = new MyLeaf("ld1A");
        MyLeaf ld1B = new MyLeaf("ld1B");
        MyLeaf ld1C = new MyLeaf("ld1C");
        d1.addNode(d1A);
        d1.addNode(d1B);
        d1.addNode(d1C);
        d1.addNode(ld1A);
        d1.addNode(ld1B);
        d1.addNode(ld1C);
 
        MyLeaf ld1Aa = new MyLeaf("ld1Aa");
        d1A.addNode(ld1Aa);
 
        MyComposite d1Ba = new MyComposite("d1Ba");
        MyComposite d1Bb = new MyComposite("d1Bb");
        MyLeaf ld1Ba = new MyLeaf("ld1Ba");
        d1B.addNode(d1Ba);
        d1B.addNode(d1Bb);
        d1B.addNode(ld1Ba);
 
        MyComposite d3A = new MyComposite("d3A");
        MyLeaf ld3A = new MyLeaf("ld3A");
        d3.addNode(d3A);
        d3.addNode(ld3A);
 
        MyVisitor v = new MyVisitor();
        int level = 1;
        r.visit(v, level);
    }
}

コンパイル+実行

> javac MyMain.java
> java MyMain
> Composite : root
>> Composite : d1
>>> Composite : d1A
>>>> Leaf : ld1Aa
>>> Composite : d1B
>>>> Composite : d1Ba
>>>> Composite : d1Bb
>>>> Leaf : ld1Ba
>>> Composite : d1C
>>> Leaf : ld1A
>>> Leaf : ld1B
>>> Leaf : ld1C
>> Composite : d2
>> Composite : d3
>>> Composite : d3A
>>> Leaf : ld3A
>> Leaf : lr1
>> Leaf : lr2

階層構造を分かりやすく表示できるようになりました。

CompositeやVisitorは、再帰や入れ子構造を使うので直感的に分かりづらいものが有ります。また、実際に使うとなると様々なパラメータ(今回なら階層の位置)を持ち回すことになりますが、どのパラメータをどのクラスに持たせ、ツリーを辿るときにどのクラスがいつ変更していくのかが悩みどころになると思います。


おまけ:昔、自分がWeb上でGOFデザインパターンを調べたとき参考にしたURLです:



プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2010-11-05 14:48:15
md5:f357dfef904315ae1781c6abde543561
sha1:fdb74dc583367235a59280d42bdcced2a32dc89d
コメント
コメントを投稿するにはログインして下さい。