2009年10月頃購入して、一年間本棚で埃を被っていたが、ようやく読み終えることが出来ました。
大学卒業後これまでの6年間、デザインパターンについてはWeb記事だったり雑誌記事のつまみ食いばかりでした。書籍として体系立てられたものを読んだことが無かったので、今までずっと独学・我流ならではの不安感があったわけです。手頃な一冊が無いかと探し、結城氏の本は分厚かったので、薄めのこちらを購入してみました。
三行レビュー:
Amazonレビューでも概ね好評、また、若干のマイナス評価についてはやはり例題が簡単すぎる点が挙げられていました。自分の読後感も的はずれでは無かったようです。
GOFの23パターンがコンパクトかつスマートにまとめられているのは、マイナス評価レビューを含めても皆さん認められているようです。難点はやはり例題が簡単すぎて却って分かりづらくないか?というところなので、その辺りをWebや雑誌記事、あるいは実際の開発現場でベテランと相談したりしつつ学んでいくと効果的だと思われます。極論を言ってしまえば、実際に使ってみて、メリットや使いすぎたときのデメリットを自分で体験してみるのが一番効果的な学習方法でしょう。
なお、本書ではデザインパターンを特に分類していません。本家での分類については適宜Wikipediaなどを参照していただくとして、読書メモとして自分なりに分類してみたのを書いておきます。
コンストラクタを直接呼ばせず、一段挟むことでわかりやすい設計にするテクニックです。
既存クラスを拡張したいときに、それを包み込むクラスを用意することで既存クラスを直接修正しなくて済むようにするテクニックです。
ツリー構造やコンテナ内の要素を順繰りに辿っていって何かさせたい時のテクニックです。再帰処理や入れ子構造を使いますので、直感的には分かりづらいかもしれません。
そのまんまです。
オブジェクト間のやりとりを上手く整理するテクニックや、使用場面がかなり絞られるテクニック(Memento, State)などです。
最後におまけとして、 "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です:
コメント