《Jython2.5》JTree〈GoF〉Composite を実現する

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

Java プログラマーのための Python 導入ガイド〈初級/応用編〉《Jython2.5》
JTree〈GoF〉Composite を実現する

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

■ 概要

フォルダー/ファイルの階層構造を「簡単に」閲覧できるツールがあると便利です。

この課題では、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

》作業中です《

Last updated♪09/06/13