2010年11月20日土曜日

第8章 Abstract Factoryパターン : 関連する部品を組み合わせて製品を作る w/ python



今回はAbstract Factoryパターン。このデザインパターンはこれまでのパターンの応用という位置づけになっている(と思う)。とはいえ、やっていることは第4章のFactory Method(http://ksk77.blogspot.com/2010/11/4-factory-method.html)の拡張で、

  • Productを複数個に増やし、
  • それらを(抽象)部品として扱い、
  • factoryMethod()をその(抽象)部品から組み立てた(抽象)製品を作るメソッドに置き換える

と考えると分かりやすい。この『部品から製品を』というところで、前章のBuilderパターンhttp://ksk77.blogspot.com/2010/11/7-builder-with-python.htmlの経験が生きてくるのかこないのか、といった感じ。

今回のデザインパターンで強く意識させられたのは、デザインパターンの大きなメリットの一つである

  • 依存しない/既存のソースコードを一切変更しない

というところ。練習問題で、これが効率・生産性・心理的にどれだけ意味があるかを実感しました。
あと、内包表記様様って感じです。


ソースコード

factory.py
# -*- coding: utf8 -*-

class Factory:
    @classmethod
    def getFactory(cls, classname):
        def get_class( kls ):
            parts = kls.split('.')
            module = ".".join(parts[:-1])
            m = __import__( module )
            for comp in parts[1:]:
                m = getattr(m, comp)
            return m
        try:
            C = get_class(classname)
            return C()
        except:
            print "invalid classname -> %s" % (classname)
            raise Exception()

    def createLink(self, caption, url): raise NotImplementedError
    def createTray(self, caption): raise NotImplementedError
    def createPage(self, title, author): raise NotImplementedError

class Item:
    def makeHTML(self): raise NotImplementedError

class Link(Item):
    def __init__(self, caption, url):
        self.caption = caption
        self.url = url

class Tray(Item):
    def __init__(self, caption):
        self.caption = caption
        self.tray = []
    def add(self, item): self.tray.append(item)

class Page:
    def __init__(self, title, author):
        self.title = title
        self.author = author
        self.contents = []

    def add(self, item): self.contents.append(item)
    def output(self): print self.makeHTML()
    def makeHTML(self): raise NotImplementedError

listfactory.py
# -*- coding: utf8 -*-

from factory import Factory, Link, Tray, Page

class ListFactory(Factory):
    def createLink(self, caption, url): return ListLink(caption, url)
    def createTray(self, caption): return ListTray(caption)
    def createPage(self, title, author): return ListPage(title, author)

class ListLink(Link):
    def makeHTML(self): return '<li><a href="%s">%s</a></li>' % (self.url, self.caption)

class ListTray(Tray):
    def makeHTML(self): return '<li>%s<ul>%s</ul></li>' % (self.caption, ''.join([i.makeHTML() for i in self.tray]))

class ListPage(Page):
    def makeHTML(self): return '<html><head><title>%s</title></head><body><ul>%s</ul><hr><address>%s</address></hr></body></html>' % (self.title, ''.join([i.makeHTML() for i in self.contents]), self.author,)

tablefactory.py
# -*- coding: utf8 -*-

from factory import Factory, Link, Tray, Page

class TableFactory(Factory):
    def createLink(self, caption, url): return TableLink(caption, url)
    def createTray(self, caption): return TableTray(caption)
    def createPage(self, title, author): return TablePage(title, author)

class TableLink(Link):
    def makeHTML(self): return '<td><a href="%s">%s</a></td>' % (self.url, self.caption)

class TableTray(Tray):
    def makeHTML(self): return '<td><table border="1" width="100%%"><tr><th colspan="%d">%s</th></tr><tr>%s</tr></table></td>' % (len(self.tray), self.caption, ''.join([i.makeHTML() for i in self.tray]))

class TablePage(Page):
    def makeHTML(self): return '<html><head><title>%s</title></head><body><table border="3"><tbody>%s</tbody></table><hr><address>%s</address></hr></body></html>' % (self.title, ''.join(['<tr>'+i.makeHTML()+'</tr>' for i in self.contents]), self.author,)

main.py
#!/usr/bin/env python
# -*- coding: utf8 -*-

from factory import Factory
import sys

def main():
    try:
        factory = Factory.getFactory(sys.argv[1])
    except:
        print "Usage: python main.py class.name.of.ConcreteFactory"
        print "Example 1: python main.py listfactory.ListFactory"
        print "Example 2: python main.py tablefactory.TableFactory"
        sys.exit(0)

    asahi = factory.createLink("朝日新聞", "http://www.asahi.com/") # Link's instance
    yomiuri = factory.createLink("読売新聞", "http://www.yomiuri.co.jp/") # Link's instance

    us_yahoo = factory.createLink("Yahoo!", "http://www.yahoo.com/") # Link's instance
    jp_yahoo = factory.createLink("Yahoo!Japan", "http://www.yahoo.co.jp/") # Link's instance
    excite = factory.createLink("Excite", "http://www.excite.com/") # Link's instance
    google = factory.createLink("Google", "http://www.google.com/") # Link's instance

    traynews = factory.createTray("新聞") # Tray's instance
    traynews.add(asahi)
    traynews.add(yomiuri)

    trayyahoo = factory.createTray("Yahoo!") # Tray's instance
    trayyahoo.add(us_yahoo)
    trayyahoo.add(jp_yahoo)

    traysearch = factory.createTray("サーチエンジン") # Tray's instance
    traysearch.add(trayyahoo)
    traysearch.add(excite)
    traysearch.add(google)

    page = factory.createPage("LinkPage", "結城 浩") # Page's instance
    page.add(traynews)
    page.add(traysearch)
    page.output()

if __name__=="__main__": main()

0 件のコメント:

コメントを投稿