《付録》dirComposite.py
# -*- coding: utf-8 -*- #=============================================================================== # Copyright (C) 2000-2008, 小泉ひよ子とタマゴ倶楽部 # # Change History: WPF examples # 2008/01/25, IronPython 1.1.1 (download) # 2008/08/22, IronPython 1.1.2 (download) # 2008/03/16, ver.2.0, WPF # 2008/00/00, ver.2.1, IronPython 1.1.2 #=============================================================================== from _ant import * from System.IO import * from System.Windows import * from System.Windows.Controls import * clr.AddReference("System.Windows.Forms") from System.Windows.Forms import OpenFileDialog from nt import listdir ## -------------------- # Composite::Component class FileSystem: def __init__(self, name=None): self.name = name self.size = 0 self.items = def total(self): return reduce( lambda acc, e: acc + e.total(), self.items, self.size) def changed(self, client): client.update( self.branch(self, 0), "%d bytes"%self.total()) def branch(self, parent, indent): s = for child in parent.items: s.append("%s+-- %s"%(" "*4*indent, child.name)) if self._isdir(child): s.append(" [%d]\n"%child.total()) s.append(self.branch(child, indent+1)) else: s.append(" [%d]\n"%child.size) return "".join(s) def _isdir(self, node): return len(node.items) ## -------------------- # Composite::Client class TreeBuilder: def __init__(self, root, path): self.clearNode(root) parent = FileSystem(self._tail(path)) self.addNode(root, parent) self.branch(parent, path, 0) def _tail(self, path): return path.split("\\")[-1] def clearNode(self, parent): parent.items.Clear() def addNode(self, parent, child): parent.items.Add(child) def branch(self, parent, path, indent): dirs = listdir(path); dirs.sort() for e in dirs: child = FileSystem(e) self.addNode(parent, child) info = FileInfo("%s\%s"%(path, e)) if self._isdir(info): self.branch(child, "%s\%s"%(path, e), indent+1) else: child.size = info.Length def _isdir(self, info): return str(FileAttributes.Directory) in str(info.Attributes) ## -------------------- class ExWindow(Window): def __init__(self, Content=None, **args): self.InitializeComponent(Content) self.init() def InitializeComponent(self, Content): self.Content = LoadXaml(Content) def init(self): target = "button", "textView", "total", self._Controls(target) self.button.Click += self.click self.root = FileSystem() self.update("(^.^)", "0 byte") def _Controls(self, target): controls = xaml_controls(self) for e in target: setattr(self, e, controls[e]) def click(self, sender, e): dialog = OpenFileDialog() dialog.ShowDialog() TreeBuilder( self.root, FileInfo(dialog.FileName).Directory.FullName) self.root.changed(self) def update(self, text, value): self.textView.Text = text self.total.Content = value ## -------------------- if __name__ == "__main__": xaml = __file__.replace(".py",".xaml") win = ExWindow( Title=__file__, Width=240, Height=200, Content=xaml, ) Application().Run(win) ## --------------------
第1章 Composite パターン 2/5
‖記事一覧‖ C#.use(better, IronPython=”WPF”)《IronPython2.6》
# IronPython で学ぶ WPF プログラミングの世界
Composite パターンには、いくつの効能があります。ここでは、if/else に象徴される典型的なハードコーディングの問題点を回避するとともに、動的な問題解決が可能になる事例を紹介します。
木構造:ファイルシステムの表現
木構造を使って、ファイルシステムを表現する方法を紹介します。
class ExWindow(Window): def init(self): target = "button", "textView", "total", self._Controls(target) self.button.Click += self.click self.root = FileSystem() self.update("(^.^)", "0 byte")
ボタン self.button をクリックすると、イベントハンドラー self.click を起動して、新たな木構造を築きます。そのツリーの頂点 self.root には、FileSystem が配置されます。ツリーを描くテキスト領域 self.textView と、フォルダーに含まれるファイルの合計サイズを示すラベル self.total には、初期値が表示 self.update されます。
def click(self, sender, e): dialog = OpenFileDialog() dialog.ShowDialog() TreeBuilder( self.root, FileInfo(dialog.FileName).Directory.FullName) self.root.changed(self)
TreeBuilder によって生成される、新たなツリーの頂点 self.root には、選択したファイル dialog.FileName を含むフォルダーが設定されます。self.root を頂点とするツリー情報をもとに、テキスト領域とラベルを更新 changed します。
1)対象となる集合体:Client
Composite::Client では、Composite::Component で規定されたプロトコルに従う、各コントロールを配置します。
## -------------------- # Composite::Client class TreeBuilder: def __init__(self, root, path): self.clearNode(root) parent = FileSystem(self._tail(path)) self.addNode(root, parent) self.branch(parent, path, 0)
選択したファイルを含むフォルダーを頂点とする、木構造を築きます。まず、指定したフォルダー path に相当する、新たな部分木 parent を用意します。次に、頂点 root の傘下に、部分木 parent を追加 addNode します。そして、parent を頂点とする新たな部分木を、フォルダー path の情報に基づいて築き branch ます。
def branch(self, parent, path, indent): dirs = listdir(path); dirs.sort() for e in dirs: child = FileSystem(e) self.addNode(parent, child) info = FileInfo("%s\%s"%(path, e)) if self._isdir(info): self.branch(child, "%s\%s"%(path, e), indent+1) else: child.size = info.Length
parent を頂点とする新たな部分木を、フォルダー path の情報に基づいて築きます。メソッド nt.listdir を利用すると、フォルダーの直下にある各項目(フォルダー/ファイル)を列挙したリスト dirs が得られます。
まず、各項目 e(フォルダー/ファイル)に相当する、新たな部分木 child を用意します。次に、頂点 parent の傘下に、部分木 child を追加 addNode します。そして、その項目 e がフォルダーなら、それを頂点とする新たな部分木を生成 branch します。その項目 e がファイルなら、そのサイズ info.Length を設定します。
《Note》楽をするための努力を惜しまない:Composite::Client の役割のひとつが、OOP のフレームワークを提供することです。ここで、if/else が必須となるのは、OOP のフレームワークを持たない外界との境界に位置して、新たなフレームワークを構築する必要があるからです。すると、境界内では、OOP の恩恵が受けられるので、冗長な if/else が不要になり、簡潔で見通しの良いコードを記述できるだけでなく、動的な問題解決が可能になります。つまり、ここで楽をするための努力を惜しまなければ、後で楽ができるというわけです。(^_^) □
》こちらに移動中です《
↑TOP