Python 弾丸ツアー #DP: ツリー ... step 3: Composite::Client

OOPデザインパターンリファクタリング

Python.use(better); Python 弾丸ツアー #DP: Tkinter/Python
>>> ツリー(階層構造)
step 3: Composite::Client
《Python3.1|Jython2.5|IronPython2.6》

《著》小粒ちゃん@湘南組《監修》小泉ひよ子とタマゴ倶楽部
第0版♪2001/03/02 ● 第1版♪2003/05/23 ● 第4版♪2010/03/08

step 3: コードの解説

■ Composite::Client
## ---------------------------------------- Composite::Client
class TIPS(object):     # Client, Aggregate
    def __init__(self, master, path):
        self.tree = Composite(path, self.tail(path))
        image_path = "../_image"
        self.dirImage  = PhotoImage(file="%s/folder.gif"%image_path)
        self.fileImage = PhotoImage(file="%s/file.gif"  %image_path)
        self.master = master
        self.path = path

    def tail(self, path):
        return path.split("/")[-1]

    def createNodes(self):
        self._createNodes(self.tree, self.path)
        self.makeTree()

    def _createNodes(self, parent, path, indent=0):
        for e in listdir(path):
            current = join(path, e)
            if isdir(current):
                component = Composite(path, e)
                parent.add_(component)
                self._createNodes(component, current, indent+1)
            else:
                component = Leaf(path, e)
                parent.add_(component)

    def makeTree(self):
        tab        = "    "
        tab_folder = "  - "
        tab_file   = "  . "

        T = self.tree
        for indent, dir, node in zip(
            T.indents(), T.dirs(), T.nodes()
        ):
            self.createNode(
                master=self.master,
                tab=tab*indent+(tab_file, tab_folder)[dir],
                image=(self.fileImage, self.dirImage)[dir],
                node=node,
                )

    def createNode(self, master, tab, image, node):
        bg   = "aliceblue"
        font = "courier 12"
        side = LEFT

        frame = Frame(master)
        frame.config(bg=bg)
        frame.pack(anchor=W)
        Label(frame, text=tab      , font=font   ).pack(side=side)
        ButtonInvoker(frame, image, receiver=node).pack(side=side)
        Label(frame, text=node.item, font=font   ).pack(side=side)

 ̄クラス TIPS では、各種コンポーネントを利用するアプリケーションを規定します。

    def makeTree(self):
        ...
            self.createNode(
                master=self.master,
                tab=tab*indent+(tab_file, tab_folder)[dir],
                image=(self.fileImage, self.dirImage)[dir],
                node=node,

 ̄キーワード引数 node= に続いて、ツリーを構成する各ノード node を指定します。

《Point》step2 以前は、
                text=self.tail(node),

Client が各ノード node を処理する必要がありました。step3 以降は、

                node=node,

各ノード node に実際の処理を依頼します。そこで、各ノードからパス名(文字列)を得る方法(how)に依存しない、その目的(what)だけを明記します。すると「仕様変更の影響」が少ない、洗練されたコードを記述できます。□

    def createNode(self, master, tab, image, node):
        ...
        Label(frame, text=tab      , font=font   ).pack(side=side)
        ButtonInvoker(frame, image, receiver=node).pack(side=side)
        Label(frame, text=node.item, font=font   ).pack(side=side)

 ̄ButtonInvoker を生成するとともに、イベントに呼応して、処理したい対象 node を指定します。

《Point》step2 以前は、
        Button(frame, image=image).pack(side=side)

既存のコンポーネント Button を利用したため、各ノードの処理を依頼できませんでした。step3 以降は、

        ButtonInvoker(frame, image, receiver=node).pack(side=side)

新規のコンポーネント ButtonInvoker に、各ノード node の処理を依頼します。すると、各ノードに依存しない独立性を確保できるので「分割統治の原則」に沿って、洗練されたコードを記述できます。□

TOP
》作業中です《