2010年11月23日火曜日

第11章 Compositeパターン : 容器と中身の同一視



第11章はCompositeパターン。これは分かりやすい上に利用頻度も高そうなデザインパターンです。
再帰的なクラス構造が出てきたら、このパターンが適用できないかを常に意識したほうがいいでしょう。

  • Leaf役とComposite役とで共通な実装にしたければComponentに実装し、
  • それぞれで違う実装にしたければComponentで定義(宣言)だけして、LeafとCompositeそれぞれで実装すればいい
という点も、コードの見通しが良くなっていいですね。

add/remove/getChild は、例と同じようにComponent(親)にエラーとして実装し、Compositeでオーバーライドするのが好きです。


実行結果
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 (31500)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (1500)
/root/usr/yuki (300)
/root/usr/yuki/diary.html (100)
/root/usr/yuki/Composite.java (200)
/root/usr/hanako (300)
/root/usr/hanako/memo.tex (300)
/root/usr/tomura (900)
/root/usr/tomura/game.doc (400)
/root/usr/tomura/junk.mail (500)

#11-2
/root (100)
/root/usr (100)
/root/usr/yuki (100)
/root/usr/yuki/Composite.java (100)
file = /root/usr/yuki/Composite.java
yuki = /root/usr/yuki

ソースコード
#!/usr/bin/env python
# -*- coding: utf8 -*-

class FileTreatmentException(Exception):
    def __init__(self):
        print "FileTreatmentException raised"


class Entry:
    def __init__(self): raise Exception("Abstract class")
    def getName(self): raise NotImplementedError
    def getSize(self): raise NotImplementedError
    def printList(self): self._printList("")
    def _printList(self, prefix): raise NotImplementedError
    def add(self, entry): raise FileTreatmentException
    def __str__(self): return "%s (%d)" % (self.getName(), self.getSize())

    #11-2
    def getFullName(self):
        entry = self
        fullname = ""
        while entry is not None:
            fullname = "/" + entry.getName() + fullname
            entry = entry.parent
        return fullname


class File(Entry):
    def __init__(self, name, size):
        self.__name = name
        self.__size = size
        self.parent = None

    def getName(self): return self.__name
    def getSize(self): return self.__size
    def _printList(self, prefix): print prefix+"/"+str(self)


class Directory(Entry):
    def __init__(self, name):
        self.__name = name
        self.directory = []
        self.parent = None

    def getName(self): return self.__name
    def getSize(self): return sum([e.getSize() for e in self.directory])

    def _printList(self, prefix):
        print prefix+"/"+str(self)
        for e in self.directory: e._printList(prefix+"/"+self.__name)

    def add(self, entry):
        self.directory.append(entry)
        entry.parent = self #11-2


def main():
    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.printList()

    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_tomura.add(File("game.doc", 400))
    m_tomura.add(File("junk.mail", 500))
    m_rootdir.printList()

    #11-2
    print ""
    print "#11-2"
    m_rootdir = Directory("root");
    m_usrdir = Directory("usr");
    m_rootdir.add(m_usrdir);
    m_yuki = Directory("yuki");
    m_usrdir.add(m_yuki);
    m_file = File("Composite.java", 100);
    m_yuki.add(m_file);
    m_rootdir.printList();
    print "file = " + m_file.getFullName()
    print "yuki = " + m_yuki.getFullName()

if __name__=='__main__': main()

0 件のコメント:

コメントを投稿