nono blog
楽しい人生を送りたい!
ITスキル

pythonプログラム デザインパターン(Abstract factory)

Factroy_Methodパターンをさらに高度化したものが、Abstract Factoryパターンとなる。

Abstract factory

(概要)
関連する部品を組み合わせて製品を作成したい。(共通性のある部品をあらかじめ生成しておき、再利用する)
この時、抽象的な工場、抽象的なぶひんを利用して、様々な製品を作成する

(イメージ)
エンジンインターフェース
>スポーツカーのエンジン
>軽自動車のエンジン

タイヤインターフェース
>スポーツカーのタイヤ
>軽自動車のタイヤ

(目的)
・複数の部品を組み合わせることで、様々な製品を作成する。
・抽象クラスを継承したぶひんを作成することで、拡張性を高めることができる。

(仕組み)
抽象クラスを継承した複数のぶひんを組み合わせて、一つの製品を作成する。
 (Factoryパターンをより複雑化したものになる)

(構成要素)
・Product      : 作成するオブジェクトの部品を定義するインターフェース
・ConcreteProduct : Productを具体化したクラス(複数作成)
・Factory      : Productを生成する処理を定義したインターフェース(抽象クラス)
・ConcreteFactory : ConcreteProductを作成するFactoryを具体化したクラス

Abstract Factoryパターンのサンプルコード

pythonによるサンプルコードをメモする。Abstract Factoryパターンのインターフェース(抽象クラス)をabstract_factory.pyに記述する。

これを concrete_factory.py にて読み込み、クラスを継承して具体的な処理を定義し、クラスの実行を行う。

# abstract_factory.py
from abc import ABC, abstractmethod


# 各製品の部品を生成するための、継承先となる抽象クラス(Product)
class AbcItem(ABC):

    def __init__(self, caption):
        self.caption = caption

    @abstractmethod
    def make_html(self):
        pass


class PageItem(AbcItem):

    def __init__(self, title, author):
        self.title = title
        self.author = author
        self.content = []

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

    def write_html(self, file_name):
        with open(file_name, 'w', encoding='utf-8') as fh:
            fh.write(self.make_html())


class LinkItem(AbcItem):
    """<a></a>"""
    def __init__(self, caption, url):
        super().__init__(caption)
        self.url = url


class ListItem(AbcItem):
    """ <li></li> """
    def __init__(self, caption):
        super().__init__(caption)
        self.items = []

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


# Productを生成する Factory の定義
class Factory(ABC):

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

    @abstractmethod
    def create_link_item(self, caption, url):
        pass

    @abstractmethod
    def create_list_item(self, caption):
        pass

abstract_facory.pyに定義したインターフェースを継承して、concrete_factory.pyで具体的な処理の記述および、クラス(部品)を使用して実行する。

# concrete_factory.py
from abstract_factory import(
    LinkItem, ListItem, PageItem, Factory
)


class HtmlPateItem(PageItem):

    def __init__(self, title, author):
        super().__init__(title, author)

    #abstractmethodの具体化
    def make_html(self):
        output = f'<html>\n<head>\n<title>{self.title}' \
                 f'</title>\n</head>\n'
        output += f'<body>\n'
        output += f'<h1>{self.title}</h1>\n'
        output += f'<ul>'
        for list_item in self.content:
            output += list_item.make_html()
        output += f'</ul>\n'
        output += f'<hr>\n<address>{self.author}</address>\n'
        output += '</body></html>'

        return output


class HtmlLinkItem(LinkItem):

    def __init__(self, caption, url):
        super().__init__(caption, url)

    def make_html(self):
        return f'<li><a href="{self.url}">{self.caption}</a></li>'


class HtmlListItem(ListItem):
    """ link item を入れる """
    def __init__(self, caption):
        super().__init__(caption)

    def make_html(self):
        output = '<li>\n'
        output += self.caption + '\n'
        output += '<ul>\n'
        for link_item in self.items:
            output += link_item.make_html()
        output += '</ul>\n'
        output += '</li>\n'

        return output


# ConcreteProductを生成する ConcreteFactory
class HtmlFactory(Factory):

    def create_page_item(self, title, author):
        return HtmlPateItem(title, author)

    def create_link_item(self, caption, url):
        return HtmlLinkItem(caption, url)

    def create_list_item(self, caption):
        return HtmlListItem(caption)


if __name__ == '__main__':

    html_factory = HtmlFactory()

    asahi = html_factory.create_link_item('Asahi News Paper', 'http://asahi')
    yomiuri = html_factory.create_link_item('Yomiuri news paper', 'http://yomiuri')
    yahoo = html_factory.create_link_item('Yahoo', 'http://yahoo')
    google = html_factory.create_link_item('Google', 'http://google')
    wikipedia = html_factory.create_link_item('Wikipedia', 'http://wikipedia')

    news_pages = html_factory.create_list_item('News Paper')
    news_pages.add(asahi)
    news_pages.add(yomiuri)

    other_pages = html_factory.create_list_item('Other pages')
    other_pages.add(yahoo)
    other_pages.add(google)
    other_pages.add(wikipedia)

    all_page = html_factory.create_page_item('My Page', 'Taro')
    all_page.add(news_pages)
    all_page.add(other_pages)

    all_page.write_html('tmp.html')