《付録》dirComposite2.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(object): def __init__(self, name): self.name = name 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)) child._branch(s, indent) return "".join(s) def _branch(self, s, indent): s.append(" [%d]\n"%self.total()) def total(self): raise NotImplementedError("%s.total" %self.__class__.__name__) ## -------------------- # Composite::Composite class Directory(FileSystem): def __init__(self, name=None): super(self.__class__, self).__init__(name) self.items = def _branch(self, s, indent): super(self.__class__, self)._branch(s, indent) s.append(self.branch(self, indent+1)) def total(self): return reduce( lambda acc, e: acc + e.total(), self.items, 0) ## -------------------- # Composite::Leaf class File(FileSystem): def __init__(self, name, size): super(self.__class__, self).__init__(name) self.size = size def total(self): return self.size ## -------------------- # Composite::Client class TreeBuilder: def __init__(self, root, path): self.clearNode(root) parent = Directory(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: info = FileInfo("%s\%s"%(path, e)) if self._isdir(info): child = Directory(e) self.branch(child, "%s\%s"%(path, e), indent+1) else: child = File(e, info.Length) self.addNode(parent, child) 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 = Directory() 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__": import sys xaml = sys.argv[1] win = ExWindow( Title=__file__, Width=240, Height=200, Content=xaml, ) Application().Run(win) ## --------------------
クラス File:Leaf
Composite::Leaf では、Composite::Leaf で規定されたプロトコルに従って、単一オブジェクトとしての機能を実現します。
## -------------------- # Composite::Leaf class File(FileSystem): def __init__(self, name, size): super(self.__class__, self).__init__(name) self.size = size
属性 self.size は、ファイルのサイズを管理します。
def total(self): return self.size
ファイルのサイズ self.size を、そのままリターン値とします。
Composite パターンを導入した後で
古典的な Composite パターンを導入した後の状態を示したのが、サンプルファイル dirComposite2.py です。
複合オブジェクトは、単一オブジェクトの集合体です。ここでは、FileSystem が、複合/単一オブジェクトに共通する特性を扱います。
class | what | how |
---|---|---|
File | ファイルに固有の特性を扱います | (親クラスから移動した)属性 size は、ファイルのサイズを管理します。 |
Directory | フォルダーに固有の特性を扱います | (親クラスから移動した)属性 items は、複数のファイル/フォルダーを管理します。属性 items に含まれる各要素を扱うときには、list で規定されたプロトコルに従います。 |
クラス Directory:Composite
Composite::Composite では、Composite::Composite で規定されたプロトコルに従って、複合オブジェクトとしての機能を実現します。
## -------------------- # Composite::Composite class Directory(FileSystem): def __init__(self, name=None): super(self.__class__, self).__init__(name) self.items = []
属性 self.items は、フォルダーに含まれる各ファイル/フォルダーを管理します。
def _branch(self, s, indent): super(self.__class__, self)._branch(s, indent) s.append(self.branch(self, indent+1))
フォルダーのサイズを表わす文字列 super()._branch の後に、その部分木を表わす文字列 self.branch を追記します。
def total(self):
return reduce(
lambda acc, e: acc + e.total(),
self.items,
0)
フォルダーに含まれる各項目(ファイル/フォルダー)のサイズ e.total() を合計したものを、リターン値とします。
クラス FileSystem:Component
## -------------------- # Composite::Component class FileSystem(object):
親クラスを object に設定すると、その子孫クラスでは、組み込み関数 super を利用して、先祖クラスのメソッドを再利用できます。
def __init__(self, name): self.name = nameself.size = 0 self.items = []
属性 size/items は、子孫クラスに移動したので、不要になります。
def branch(self, parent, indent):
s = []
for child in parent.items:
s.append("%s+-- %s"%(" "*4*indent, child.name))
child._branch(s, indent)
return "".join(s)
各 child が、フォルダーかファイルかを判定する if/else が不要になるので、簡潔で見通しの良いコードを記述できます。
def _branch(self, s, indent):
s.append(" [%d]\n"%self.total())
どのようにして合計サイズを得るか total() は、子孫クラスに依存します。そのため、動的な問題解決が可能になります。
def total(self): raise NotImplementedError("%s.total" %self.__class__.__name__)
ここで規定したメソッドを、子孫クラスで再定義しないと、実行時に例外 NotImplementedError を生成するとともに、そのクラス名を出力します。