例題で学ぶデザインパターン #14: デザインパターン〈GoF〉Composite, #2
例題で学ぶ Jython/Swing デザインパターン《Jython2.5》
デザインパターン〈GoF〉Composite
■ 概要
例題により、アプリケーションを作成する過程を通して、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
》作業中です《