例題で学ぶデザインパターン #14: デザインパターン〈GoF〉Composite, #2

前の記事記事一覧次の記事

例題で学ぶ Jython/Swing デザインパターン《Jython2.5》
デザインパターンGoF〉Composite

《著》越智ことり+小粒ちゃん《監修》小泉ひよ子とタマゴ倶楽部
第1版♪2003/05/23 ● 第2版♪2009/04/03

■ 概要

例題により、アプリケーションを作成する過程を通して、Jython/Swing によるデザインパターンを習得します。

この課題では、Swing/GUI を使って階層構造を持つ情報を提示します。〈GoF〉Composite/Iterator/Visitor/Command パターンを導入すると、if/for 文によるコードの汚染、配列の境界問題が解消されるので、要求仕様の変更にも柔軟に対処でき、簡潔で見通しの良いコードを記述できるようになります。

《Note》JPython1.1.x/Jython2.1.x 用に作成したセミナー課題を、Jython2.5 で再構成しました。

事例:コードの解説

■ モジュール:TreeComposite.py
class TreeNode(object):

クラス TreeNode では、子孫クラス Leaf/Node に共通するプロトコルを規定します。

    def __init__(self, node, *args, **keys):
        self.name = str(node)
        self.items = []
    def __repr__(self):
        return self.name

インスタンス属性 self.name は、各ノードを識別する名前(文字列)を保持します。インスタンス属性 self.items は、その傘下にある子ノード Leaf/Node を管理します。

    def __iter__(self):
        for e in self.treeNode.children():
            yield e.userObject

メソッド __iter__ では、ツリーの各ノードが保持するモデル e.userObject を順に参照します。特定のデータ構造(DefaultMutableTreeNode)に依存する部分は、このメソッドに集約されるので、他のメソッドは実現方法に依存しない抽象表現が可能です。

    def __len__(self):
        return reduce(
            lambda s, e: s + (e) + 1,
            self,
            0)
    def children(self):
        return reduce(
            lambda s, e: s + e.children(),
            self,
            [self])
    def levels(self, level=0):
        return reduce(
            lambda s, e: s + e.levels(level+1),
            self,
            [level])

変更したメソッド __iter__ の効能によって、ツリーの各ノードが保持するモデルを参照するときには、単に e と記述するだけです。簡潔で見通しの良いコードになるだけでなく、実現方法に依存しない抽象表現が可能になります。

《Note》これらの抽象メソッドは、特定の実現方法に依存するコードを含みません。引数を介して、外部から得られる情報を利用するだけです。メソッド本体では、メソッド自身を再帰的に呼び出すだけです。そして、各要素 e を参照する実現手段は、self を介して、メソッド __iter__ 内に隠蔽されます。

    def add(self, node):
        self.items.append(node)

新たなメソッド add は、要素 node を追加する方法を規定します。

class Leaf(TreeNode):
    def __str__(self):
        return "/".join([str(e) for e in list(self.treeNode.path)])

class Node(TreeNode):
    def __str__(self):
        return "\n".join([
            "%s%s%r (%s)"%(
                "|   "*(level-1),
                "+-- "*(not not level),
                node,
                len(node),
            ) for node, level in zip(self.children(), self.levels())
            ])

前述した モジュール とは違って、if..else が不要になります。クラス Leaf/Node では、他のクラスの存在を気に掛ける必要がなく、相互に独立したメソッドを規定する(関心分離の原則)だけです。

■ モジュール:TreeView.py
class View(JPanel):
    def init_splitPane(self):
        tree = JTree()
        model = Model(tree.model.root)
        self.tree = JTree(
            model.root.treeNode,
            valueChanged = self.valueChanged,
            )
        ...

    def valueChanged(self, e):
        node = e.path.lastPathComponent
        self.textArea.text = str(node.userObject)

    def actionPerformed(self, e):
        path = self.tree.selectionPath.lastPathComponent
        model = Model(path)
        view = JTree(
            model.root.treeNode,
            valueChanged = self.valueChanged,
            )
        ...

Tips

》作業中です《

Last updated♪09/06/14