気がつけば、もう本の半分以上の量を消化していました。
今回はVisitorパターン。キーワードは『データ構造と処理を分離する』です。
例えば、データモデルクラスを作って、その中にデータ処理のメソッドなんかを追加すると、モデルクラスが肥大してしまうことがままありました。
(アクセサぐらいならまだいいのですが、シリアライザやパーサなんかもついつい入れがちでした)
そんな時は大抵別のクラスを用意してメソッド類を引越ししてやるのですが、色んなモデルクラスの色んな処理クラスが混在して見通しが悪くなりがちでした。
Visitorパターンは、こうしたケースに一貫した解決を与えるいい方法ではないかと思いました。
また書籍内の例を見ても分かりますが、CompositeパターンやDecoratorパターンなどの再帰的なパターンと相性が良さそうです。
--
visit()のオーバーロードは特に意味が無いようなので、メソッドを分けてあります。
iterator()は単にリストを返すようにしてあります。
練習問題13-1には、対象のファイルが存在するディレクトリのパスも併せて出力するように勝手に変更 してあります。
実行結果
Making root entries... /root (30000) /root/bin (30000) /root/bin/vi (10000) /root/bin/latex (20000) /root/tmp (0) /root/usr (0) Making user entries... /root (31850) /root/bin (30000) /root/bin/vi (10000) /root/bin/latex (20000) /root/tmp (0) /root/usr (1850) /root/usr/yuki (300) /root/usr/yuki/diary.html (100) /root/usr/yuki/Composite.java (200) /root/usr/hanako (650) /root/usr/hanako/memo.tex (300) /root/usr/hanako/index.html (350) /root/usr/tomura (900) /root/usr/tomura/game.doc (400) /root/usr/tomura/junk.mail (500) HTML files are: diary.html (100) in /root/usr/yuki index.html (350) in /root/usr/hanako
ソースコード
#!/usr/bin/env python # -*- coding: utf8 -*- class FileTreatmentException(Exception): def __init__(self): pass class __Element: def __init__(self): raise NotImplementedError def accept(self, visitor): raise NotImplementedError class _Entry(__Element): def __init__(self): raise Exception("Abstract class") def getName(self): raise NotImplementedError def getSize(self): raise NotImplementedError def add(self, entry): raise FileTreatmentException def iterator(self): raise FileTreatmentException def __str__(self): return "%s (%d)" % (self.getName(), self.getSize()) class File(_Entry): def __init__(self, name, size): self.__name = name self.__size = size def getName(self): return self.__name def getSize(self): return self.__size def accept(self, visitor): visitor.visitFile(self) class Directory(_Entry): def __init__(self, name): self.__name = name self.directory = [] def getName(self): return self.__name def getSize(self): return sum([e.getSize() for e in self.directory]) def add(self, entry): self.directory.append(entry) def iterator(self): return self.directory def accept(self, visitor): visitor.visitDirectory(self) class _Visitor: def __init__(self): raise Exception("Abstract class") def visitFile(self, thefile): raise NotImplementedError def visitDirectory(self, directory): raise NotImplementedError class ListVisitor(_Visitor): def __init__(self): self.__currentdir = "" def visitFile(self, thefile): print self.__currentdir+"/"+str(thefile) def visitDirectory(self, directory): print self.__currentdir+"/"+str(directory) # ディレクトリ内を走査するために一旦入る savedir = self.__currentdir self.__currentdir = self.__currentdir+"/"+directory.getName() for e in directory.iterator(): e.accept(self) # 走査が終わったらディレクトリを出る self.__currentdir = savedir #13-1 class FileFindVisitor(_Visitor): def __init__(self, pattern): self.__pattern = pattern self.__currentdir = "" self.__founds = [] #(file, dirname which contains the file) def visitFile(self, thefile): if thefile.getName().find(self.__pattern) is not -1: self.__founds.append((thefile, self.__currentdir)) def visitDirectory(self, directory): savedir = self.__currentdir self.__currentdir = self.__currentdir+"/"+directory.getName() for e in directory.iterator(): e.accept(self) self.__currentdir = savedir def getFoundFiles(self): return self.__founds def main(): try: print "Making root entries..." m_rootdir = Directory("root") m_bindir = Directory("bin") m_tmpdir = Directory("tmp") m_usrdir = Directory("usr") m_rootdir.add(m_bindir) m_rootdir.add(m_tmpdir) m_rootdir.add(m_usrdir) m_bindir.add(File("vi", 10000)) m_bindir.add(File("latex", 20000)) m_rootdir.accept(ListVisitor()) print "" print "Making user entries..." m_yuki = Directory("yuki") m_hanako = Directory("hanako") m_tomura = Directory("tomura") m_usrdir.add(m_yuki) m_usrdir.add(m_hanako) m_usrdir.add(m_tomura) m_yuki.add(File("diary.html", 100)) m_yuki.add(File("Composite.java", 200)) m_hanako.add(File("memo.tex", 300)) m_hanako.add(File("index.html", 350)) m_tomura.add(File("game.doc", 400)) m_tomura.add(File("junk.mail", 500)) m_rootdir.accept(ListVisitor()) #13-1 print "" print "HTML files are:" ffv = FileFindVisitor(".html") m_rootdir.accept(ffv) for (f,d) in ffv.getFoundFiles(): print "%s in %s" % (str(f), d) except FileTreatmentException: print "FileTreatmentException raised" if __name__=='__main__': main()
0 件のコメント:
コメントを投稿