《Jython2.5》JTree〈GoF〉Composite を実現する
Java プログラマーのための Python 導入ガイド〈初級/応用編〉《Jython2.5》
JTree〈GoF〉Composite を実現する
■ 概要
フォルダー/ファイルの階層構造を「簡単に」閲覧できるツールがあると便利です。
この課題では、Swing/GUI を使って階層構造を持つ情報を提示します。〈GoF〉Composite/Iterator/Visitor/Command パターンを導入すると、if/for 文によるコードの汚染、配列の境界問題が解消されるので、要求仕様の変更にも柔軟に対処でき、簡潔で見通しの良いコードを記述できるようになります。
《Note》JPython1.1.x/Jython2.1.x 用に作成したセミナー課題を、Jython2.5 で再構成しました。
事例:コードの解説
■ フォルダーに含まれる項目数:__len__
組み込み関数 len に呼応して、フォルダーに含まれる項目の数が得られます。
class FileNode(Node): # Composite::Leaf def __len__(self): return 0
ファイルは、項目を含まないので、0 をリターン値にします。
class DirNode(Node): # Composite::Composite def __len__(self): return reduce( lambda s,e: s+len(e.userObject)+1, self.node.children(), 0)
フォルダーは、そこに含まれるファイルだけでなく、別の項目を含むフォルダーも考慮する必要があります。そこで、この問題に対処するために、メソッド getsize を再帰的に呼び出します。
■ ファイル/フォルダーのサイズ
メソッド getsize を利用すると、フォルダーに含まれるファイルのサイズを合計した値が得られます。
from os.path import getsize class FileNode(Node): # Composite::Leaf def getsize(self): return getsize(self.path)
ファイルは、項目を含まないので、0 をリターン値にします。
class DirNode(Node): # Composite::Composite def getsize(self): return reduce( lambda s,e: s+e.userObject.getsize(), self.node.children(), 0)
フォルダーは、そこに含まれるファイルだけでなく、別の項目を含むフォルダーも考慮する必要があります。そこで、この問題に対処するために、メソッド count を再帰的に呼び出します。
《Note》os.path.getsize と同じ名前にしたのは、作為的です。すると、組み込みモジュールと、新規モジュールとの違いを意識せずに、統一したコードを記述できます。
■ ツリー表示
メソッド showTextTree を利用すると、ツリーを表現する文字列が得られます。
class Node(object): # Composite::Component tab1 = "| " tab2 = "+-- " def showTextTree(self): return "\n".join(self.textTree()) class FileNode(Node): # Composite::Leaf def textTree(self, indent=0): return ["%s%s%r (%d)"%( self.tab1*(indent-1), self.tab2, self, self.getsize())] class DirNode(Node): # Composite::Composite def textTree(self, indent=0): return reduce( lambda s,e: s+(e.userObject.textTree(indent+1)), self.node.children(), ["%s%s%r"%( self.tab1*(indent-1), self.tab2*(not not indent), self)])
何が問題か:what vs. how
ツリーを表現する文字列をどのように生成するか(how)は、何を表現したいか(what)とは独立した問題です。これらが混在していると、要求仕様が変更されるたびに、コードの改編を余儀なくされます。そこで〈GoF〉Visitor パターンを導入して、これらの問題を解消できるようにします。
⇒ 続きはこちら 》Visitor を実現する《 で
misc
■ ノードに表示される文字列:__repr__
組み込み関数 repr に呼応して、各ノードに表示する文字列が得られます。
## ---------------------------------------- class Node(object): # Composite::Component def __init__(self, path): self.path = path def __repr__(self): return self.path.split("/")[-1]
■ ノードに固有の文字列表現:__str__
組み込み関数 str に呼応して、各ノードに固有の文字列表現が得られます。
class FileNode(Node): # Composite::Leaf def __str__(self): s = s.append("%r\n"%self) path = self.path s.append("size: %d" %self.getsize()) s.append("access:\n\t%s" %ctime(getatime(path))) s.append("change:\n\t%s" %ctime(getctime(path))) s.append("modification:\n\t%s"%ctime(getmtime(path))) return "\n".join(s) class DirNode(Node): # Composite::Composite def __str__(self): s = s.append("%r\n"%self) s.append("size: %d (%d items)"%(self.getsize(), len(self))) s.append("path: %s"%self.path) return "\n".join(s)
Tips
》作業中です《