3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Pythonで、デザインパターン「Abstract Factory」を学ぶ

Last updated at Posted at 2020-01-12

GoFのデザインパターンを学習する素材として、書籍「増補改訂版Java言語で学ぶデザインパターン入門」が参考になるみたいですね。ただ、取り上げられている実例は、JAVAベースのため、自分の理解を深めるためにも、Pythonで同等のプラクティスに挑んでみました。

■ Abstract Factory(アブストラクトファクトリ)

Abstract Factory パターン(アブストラクト・ファクトリ・パターン)とは、GoF(Gang of Four; 4人のギャングたち)によって定義されたデザインパターンの1つである。 関連するインスタンス群を生成するための API を集約することによって、複数のモジュール群の再利用を効率化することを目的とする。日本語では「抽象的な工場」と翻訳される事が多い。Kit パターンとも呼ばれる

UML class and sequence diagram

W3sDesign_Abstract_Factory_Design_Pattern_UML.jpg

UML class diagram

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3130333539352f62393731323034392d616664392d323330332d653539372d3031393136333530633732382e706e67.png
(以上、ウィキペディア(Wikipedia)より引用)

■ "Abstract Factory"のサンプルプログラム

Abstract Factoryパターンとは、抽象的な工場から、抽象的な部品を組み合わせて抽象的な製品を作るらしいです。
実際に、Abstract FactoryのPython実装コードを動かしてみて、抽象的な工場をイメージを理解したいと思います。ここで取り上げるサンプルプログラムは、階層構造を持ったリンク集をHTMLファイルとして作るものです。

  • ListFactoryモードで、動作させると、リスト形式のリンク集のHTMLファイルが生成されます。
  • TableFactoryモードで、動作させると、テーブル形式のリンク集のHTMLファイルが生成されます。

(1) ListFactoryを動かしてみる

まずは、LinkベースのWebページを作成するコードを動かしてみます。

$ python Main.py ListFactory
[LinkPage.html] was created.

LinkPage.htmlというファイルが生成されました。
Webブラウザで、見た目を確認してみると、こんな感じでした。
listfactory.png

(2) TableFactoryを動かしてみる

つづいて、TableベースのWebページを作成するコードを動かしてみます。

$ python Main.py TableFactory
[LinkPage.html] was created.

LinkPage.htmlというファイルが生成されました。
Webブラウザで、見た目を確認してみると、こんな感じでした。
tablefactory.png

■ サンプルプログラムの詳細

Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern/tree/master/AbstractFactory

  • ディレクトリ構成
.
├── Main.py
└── factory
    ├── __init__.py
    ├── factory.py
    ├── listfactory
    │   ├── __init__.py
    │   └── list_factory.py
    └── tablefactory
        ├── __init__.py
        └── table_factory.py

(1) AbstractProduct(抽象的な製品)の役

AbstractProduct役は、AbstractFactory役によって、作り出される抽象的な部品や製品のインタフェースを定めます。サンプルプログラムでは、Linkクラス、Trayクラス、Pageクラスが、この役を努めます。

factory/factory.py
import sys
from abc import ABCMeta, abstractmethod

... (snip)

class Item(metaclass=ABCMeta):
    def __init__(self, caption):
        self.caption = caption

    @abstractmethod
    def makeHtml(self):
        pass

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

class Tray(Item, metaclass=ABCMeta):
    def __init__(self, caption):
        super().__init__(caption)
        self.tray = []

    def add(self, item):
        self.tray.append(item)

class Page(metaclass=ABCMeta):
    def __init__(self, title, author):
        self.title = title
        self.author = author
        self.content = []

    def add(self, item):
        self.content.append(item)

    def output(self):
        try:
            filename = self.title + '.html'
            writer = open(filename, 'w')
            writer.write(self.makeHtml())
            writer.close()
            print("[" + filename + "]" + " was created.")
        except Exception as e:
            print(e)
            sys.exit(1)

    @abstractmethod
    def makeHtml(self):
        pass

(2) AbstractFactory(抽象的な工場)の役

AbstractFactory役は、AbstractProduct役のインスタンスを作り出すためのインタフェースを定めます。
サンプルプログラムでは、Factoryクラスがこの役を勤めます。

factory/factory.py
import sys
from abc import ABCMeta, abstractmethod

class Factory(metaclass=ABCMeta):
    @abstractmethod
    def createLink(self, caption, url):
        pass

    @abstractmethod
    def createTray(self, caption):
        pass

    @abstractmethod
    def createPage(self, title, author):
        pass

... (snip)

(3) Client(依頼者)の役

Client役は、AbstractFactory役とAbstractProduct役のインタフェースだけを使って仕事を行います。
Client役は、具体的な部品や製品や工場については、知りません。なお、サンプルプログラムでは、startMain メソッドが、この役を努めます。

Main.py
import sys
import inspect
import factory

def startMain(factoryObject):
    asahi = factoryObject.createLink("Asahi", "http://www.asahi.com")
    yomiuri = factoryObject.createLink("Yomiuri", "http://www.yomiuri.co.jp")
    us_yahoo = factoryObject.createLink("Yahoo", "http://www.yahoo.com")
    jp_yahoo = factoryObject.createLink("Yahoo!Japan", "http://www.yahoo.co.jp")
    google = factoryObject.createLink("Google", "http://www.google.com")
    excite = factoryObject.createLink("Excite", "http://www.excite.co.jp")

    traynews = factoryObject.createTray("Newspaper")
    traynews.add(asahi)
    traynews.add(yomiuri)

    trayyahoo = factoryObject.createTray("Yahoo!")
    trayyahoo.add(us_yahoo)
    trayyahoo.add(jp_yahoo)

    traysearch = factoryObject.createTray("Search Engine")
    traysearch.add(trayyahoo)
    traysearch.add(excite)
    traysearch.add(google)

    page = factoryObject.createPage("LinkPage", "Hiroshi Yuki")
    page.add(traynews)
    page.add(traysearch)
    page.output()

if __name__ == '__main__':
    for _, plugin in inspect.getmembers(factory, inspect.isclass):
        if plugin.__name__ == sys.argv[1]:
            startMain(plugin())

(4) ConcreteProduct(具体的な製品)の役

ConcreteProduct役は、AbstractProduct役のインタフェースを実装します。サンプルプログラムでは、以下のクラスが、この役を努めます。

  • ListLinkクラス、ListTrayクラス、ListPageクラス
  • TableLinkクラス、TableTrayクラス、TablePageクラス
factory/listfactory/list_factory.py
from factory.factory import Factory, Link, Tray, Page

... (snip)

class ListLink(Link):
    def __init__(self, caption, url):
        super().__init__(caption, url)

    def makeHtml(self):
        return '  <li><a href="{}">{}</a></li>\n'.format(self.url, self.caption)

class ListTray(Tray):
    def __init__(self, caption):
        super().__init__(caption)

    def makeHtml(self):
        buf = []
        buf.append('<li>\n')
        buf.append(self.caption + '\n')
        buf.append('<ul>\n')

        for item in self.tray:
            buf.append(item.makeHtml())

        buf.append('</ul>\n')
        buf.append('</li>\n')
        return ''.join(buf)

class ListPage(Page):
    def __init__(self, title, author):
        super().__init__(title, author)

    def makeHtml(self):
        buf = []
        buf.append('''
<html>
  <head><title>{}</title></head>
'''.format(self.title))
        buf.append('<body>\n')
        buf.append('<h1>{}</h1>'.format(self.title))
        buf.append('<ul>')

        for item in self.content:
            buf.append(item.makeHtml())

        buf.append('</ul>')
        buf.append('<hr><adress>{}</adress>'.format(self.author))
        buf.append('</body>\n</html>\n')
        return ''.join(buf)
factory/tablefactory/table_factory.py
from factory.factory import Factory, Link, Tray, Page

... (snip)

class TableLink(Link):
    def __init__(self, caption, url):
        super().__init__(caption, url)

    def makeHtml(self):
        return '<td><a href={}>{}</a></td>'.format(self.url, self.caption)

class TableTray(Tray):
    def __init__(self, caption):
        super().__init__(caption)

    def makeHtml(self):
        buf = []
        buf.append('<td>')
        buf.append('<table width="100%" border="1"><tr>')
        buf.append('<td bgcolor="#cccccc" algin="center" colsapn="{}"><b>{}</b></td>'.format(len(self.tray), self.caption))
        buf.append('</tr>\n')
        buf.append('<tr>\n')

        for item in self.tray:
            buf.append(item.makeHtml())

        buf.append('</tr></table>')
        buf.append('</td>')
        return ''.join(buf)

class TablePage(Page):
    def __init__(self, title, author):
        super().__init__(title, author)

    def makeHtml(self):
        buf = []
        buf.append('''
<html>
  <head><title>{}</title></head>
    '''.format(self.title))
        buf.append('<body>\n')
        buf.append('<h1>{}</h1>'.format(self.title))
        buf.append('<table width="80%" border="3">\n')

        for item in self.content:
            buf.append('<tr>{}</tr>'.format(item.makeHtml()))

        buf.append('</table>')
        buf.append('<hr><adress>{}</adress>'.format(self.author))
        buf.append('</body>\n</html>\n')
        return ''.join(buf)

(5) ConcreteFactory(具体的な工場)の役

ConcreteFactory役は、AbstractFactory役のインタフェースを実装します。サンプルプログラムでは、以下のクラスが、この役を努めます。

  • ListFactoryクラス
  • TableFactoryクラス
factory/__init__.py
from factory.listfactory.list_factory import ListFactory
from factory.tablefactory.table_factory import TableFactory

__all__ = [
    "ListFactory",
    "TableFactory"   
]
factory/listfactory/list_factory.py
from factory.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)

... (snip)
factory/tablefactory/table_factory.py
from factory.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)

... (snip)

■ 参考URL

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?