気がつけば、もう本の半分以上の量を消化していました。
今回は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 件のコメント:
コメントを投稿