Abstract Factoryパターン
結城浩さんの「Java言語で学ぶデザインパターン入門」を題材にpythonでデザインパターンを書いてみる。
今回の題材はAbstract Factoryパターン。名前の通り抽象的な工場で、抽象的な部品を組み合わせて抽象的な製品を作るらしい。
わかりにくいが、要は部品の具体的な実装は気にせずに部品を組み立てて製品としてまとめてしまうというものみたい。
具体的な実装はサブクラスで行う。
サンプルのプログラムは階層構造を持ったリンク集をHTMLファイルとして作るもの。
はじめに抽象的な工場、部品、製品を含む部分(factory.py)。
# -*- coding: utf-8 -*- class Item(object): def __init__(self, caption): self.caption = caption def makeHTML(self): pass class Link(Item): def __init__(self, caption, url): super(Link, self).__init__(caption) self.url = url class Tray(Item): def __init__(self, caption): super(Tray, self).__init__(caption) self.tray = [] def add(self, item): self.tray.append(item) class Page(object): def __init__(self, title, author): self.title = title self.author = author self.content = [] def add(self, item): self.content.append(item) def output(self): self.filename = "%s.html" %self.title writer = open(self.filename, "w") writer.write(self.makeHTML()) writer.close() print "%sを作成しました。" %self.filename def makeHTML(self): pass class Factory(object): @classmethod def getFactory(cls, classname): module, kls = classname.rsplit(".", 1) return getattr(__import__(module), kls)() def createLink(self, caption, url): pass def createTray(self, caption): pass def createPage(self, title, author): pass
ItemクラスはLinkクラスとTrayクラスを統一的に扱うために両者のスーパークラスになる。
LinkクラスはHTMLのリンクを抽象的に表現したクラス。Trayクラスはaddメソッドを使って複数の項目をひとまとめにする。
PageクラスはHTMLページ全体を抽象的に表現したクラス。addメソッドで項目を追加、outoutメソッドで自分自身の内容を
ファイルに書き込む。
Factoryクラスは抽象的な工場。getFactoryメソッドはクラス名を指定して工場のインスタンスをつくる。
getattr()関数は指定したオブジェクトの属性を返してくる。ここではclassnameで指定されたクラスのモジュールをインポート
して、モジュール内のクラスのインスタンスが返り、そのインスタンスを新しくつくってgetFactoryメソッドの戻り値にしている。
次に動作テスト用の実行部分(abstract_factory_main.py)。
# -*- coding: utf-8 -*- from factory import * import sys factory = Factory.getFactory(sys.argv[1]) asahi = factory.createLink("朝日新聞", "http://www.asahi.com/") yomiuri = factory.createLink("読売新聞", "http://www.yomiuri.co.jp/") us_yahoo = factory.createLink("Yahoo!", "http://www.yahoo.com/") jp_yahoo = factory.createLink("Yahoo!Japan", "http://www.yahoo.co.jp/") excite = factory.createLink("Exite", "http://www.excite.com/") google = factory.createLink("Google", "http://www.google.com/") traynews = factory.createTray("新聞") traynews.add(asahi) traynews.add(yomiuri) trayyahoo = factory.createTray("Yahoo!") trayyahoo.add(us_yahoo) trayyahoo.add(jp_yahoo) traysearch = factory.createTray("サーチエンジン") traysearch.add(trayyahoo) traysearch.add(excite) traysearch.add(google) page = factory.createPage("LinkPage", "name") page.add(traynews) page.add(traysearch) page.output()
抽象的な工場で抽象的な部品をつくり、抽象的な製品を組み立てる。具体的な工場のクラス名はコマンドラインの引数で
指定する。この引数からgetFactoryで工場をつくって変数factoryに代入する。後はLink,Trayをつくってひとまとめにして
から、Pageをつくってoutput。
ここからは具体的な工場、部品、製品を含む部分(listfactory.py)。
import factory class ListFactory(factory.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(factory.Link): def __init__(self, caption, url): super(ListLink, self).__init__(caption, url) def makeHTML(self): return ' <li><a href="%s">%s</a></li>\n' %(self.url, self.caption) class ListTray(factory.Tray): def __init__(self, caption): super(ListTray, self).__init__(caption) def makeHTML(self): self.buffer = [] self.buffer.append("<li>\n") self.buffer.append("%s\n" %self.caption) self.buffer.append("<ul>\n") for item in self.tray: self.buffer.append(item.makeHTML()) self.buffer.append("</ul>\n") self.buffer.append("</li>\n") return "\n".join(self.buffer) class ListPage(factory.Page): def __init__(self, title, author): super(ListPage, self).__init__(title, author) def makeHTML(self): self.buffer = [] self.buffer.append("<html><head><title>%s</title></head>\n" %self.title) self.buffer.append("<body>\n") self.buffer.append("<h1>%s</h1>\n" %self.title) self.buffer.append("<ul>\n") for item in self.content: self.buffer.append(item.makeHTML()) self.buffer.append("</ul>\n") self.buffer.append("<hr><address>%s</address>" %self.author) self.buffer.append("</body></html>\n") return "\n".join(self.buffer)
ListFactoryクラスはFactoryクラスのcreateLink,createTray,createPageを実装。単にListLink,ListTray,ListPageのインスタンス
を返すだけ。
ListLinkクラスはmakeHTMLメソッドを実装してHTMLの断片をつくる。ListTrayクラスもmakeHTMLメソッドを実装。HTMLの断片をリストに
集めて最後につなげる。ListPageクラスでもmakeHTMLメソッドを実装してページの構成をつくる。
最後に別の具体的な工場を含む部分(tablefactory.py)。
import factory class TableFactory(factory.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(factory.Link): def __init__(self, caption, url): super(TableLink, self).__init__(caption, url) def makeHTML(self): return '<td><a href="%s">%s</a></td>\n' %(self.url, self.caption) class TableTray(factory.Tray): def __init__(self, caption): super(TableTray, self).__init__(caption) def makeHTML(self): self.buffer = [] self.buffer.append("<td>") self.buffer.append('<table width="100%" border="1"><tr>') self.buffer.append('<td bgcolor="#cccccc" align="center" colspan="%s"><b>%s</b></td>' %(len(self.tray), self.caption)) self.buffer.append("</tr>\n") self.buffer.append("<tr>\n") for item in self.tray: self.buffer.append(item.makeHTML()) self.buffer.append("</tr></table>") self.buffer.append("</td>") return "\n".join(self.buffer) class TablePage(factory.Page): def __init__(self, title, author): super(TablePage, self).__init__(title, author) def makeHTML(self): self.buffer = [] self.buffer.append("<html><head><title>%s</title></head>\n" %self.title) self.buffer.append("<body>\n") self.buffer.append("<h1>%s</h1>\n" %self.title) self.buffer.append('<table width="80%" boder="3">\n') for item in self.content: self.buffer.append("<tr>%s</tr>" %item.makeHTML()) self.buffer.append("</table>\n") self.buffer.append("<hr><address>%s</address>" %self.author) self.buffer.append("</body></html>\n") return "\n".join(self.buffer)
実際に実行するときは、
C:\works\python\book1>python abstract_factory_main.py listfactory.ListFactory LinkPage.htmlを作成しました。
とすると、箇条書きを元にしたデザインのHTMLファイルがつくられる。
C:\works\python\book1>python abstract_factory_main.py tablefactory.TableFactory LinkPage.htmlを作成しました。
とすると、表組みを元にしたデザインになる。