📖

Abstract Factoryパターンを学ぶ【Python】

2022/02/16に公開

一言で言うと

抽象的な工場/部品/製品を具体的な工場/部品/製品を作成。
・具体的な工場を複数作ることができる。
・具体的な工場に全てに新しい部品を追加しないといけないので、新しい部品を作るのは難しい.
インスタンスを作る部分はFactoryクラス。

コードサンプル

ディレクトリ構造
abstract_factory
├── factory
│   ├── factory.py
│   ├── item.py
│   ├── link.py
│   ├── page.py
│   └── tray.py
├── listfactory
│   ├── list_factory.py
│   ├── list_link.py
│   ├── list_page.py
│   └── list_tray.py
└── main.py
main.py
import sys

from factory.link import Link
from factory.page import Page
from factory.tray import Tray
from factory.factory import Factory


def main():
    args = sys.argv
    if len(args) != 2:
        print('Usage: python      moduledirectory.modulename.ConcreteFactory')
        print('Example 1: python  moduledirectory.modulename.ListFactory')
        exit(0)

    factory: Factory = Factory.get_factory(args[1])

    asahi: Link = factory.create_link('朝日新聞', 'https://www.asahi.com/')
    yomiuri: Link = factory.create_link('読売新聞', 'https://www.yomiuri.co.jp/')

    traynews: Tray = factory.create_tray('新聞')
    traynews.add(asahi)
    traynews.add(yomiuri)

    page: Page = factory.create_page('LinkPage', '著者')
    page.add(traynews)
    page.output()


if __name__ == '__main__':
    main()
factory.py
from __future__ import annotations
from abc import ABCMeta, abstractmethod
from importlib import import_module

from factory.link import Link
from factory.page import Page
from factory.tray import Tray


class Factory(metaclass=ABCMeta):
    @classmethod
    def get_factory(cls, class_name: str) -> Factory:
        module, class_ = class_name.rsplit('.', 1)
        return getattr(import_module(module), class_)()

    @abstractmethod
    def create_link(self, caption: str, url: str) -> Link:
        pass

    @abstractmethod
    def create_tray(self, caption: str) -> Tray:
        pass

    @abstractmethod
    def create_page(self, title: str, author: str) -> Page:
        pass
item.py

from abc import ABCMeta, abstractmethod


class Item(metaclass=ABCMeta):
    def __init__(self, caption: str) -> None:
        self._caption: str = caption

    @abstractmethod
    def make_html(self) -> str:
        pass
link.py
from .item import Item


class Link(Item):
    def __init__(self, caption: str, url: str) -> None:
        super().__init__(caption)
        self._url = url
page.py
from abc import ABCMeta, abstractmethod
from .item import Item


class Page(metaclass=ABCMeta):
    _content: list = []

    def __init__(self, title: str, author: str) -> None:
        self.title: str = title
        self.author: str = author

    def add(self, item: Item) -> None:
        self._content.append(item)

    def output(self) -> None:
        filename: str = f'{self.title}.html'
        with open(filename, mode='w') as f:
            f.write(self.make_html())
            print(f'{filename}を作成しました。')

    @abstractmethod
    def make_html(self) -> str:
        pass
tray.py
from .item import Item


class Tray(Item):
    _tray: list = []

    def __init__(self, caption: str) -> None:
        super().__init__(caption)

    def add(self, item: Item) -> None:
        self._tray.append(item)
list_factory.py
from listfactory.list_page import ListPage
from listfactory.list_tray import ListTray
from factory.factory import Factory
from factory.link import Link
from factory.page import Page
from factory.tray import Tray
from listfactory.list_link import ListLink


class ListFactory(Factory):
    def create_link(self, caption: str, url: str) -> Link:
        return ListLink(caption, url)

    def create_tray(self, caption: str) -> Tray:
        return ListTray(caption)

    def create_page(self, title: str, author: str) -> Page:
        return ListPage(title, author)
list_link.py
from factory.link import Link


class ListLink(Link):
    def __init__(self, caption: str, url: str) -> None:
        super().__init__(caption, url)

    def make_html(self) -> str:
        return f'  <li><a href="{self._url}">{self._caption}</a></li>'
list_page.py
from factory.page import Page


class ListPage(Page):
    def __init__(self, title: str, author: str) -> None:
        super().__init__(title, author)

    def make_html(self) -> str:
        str_buf: list = []

        str_buf.append('<html><head><title></title></head>')
        str_buf.append('<body>')
        str_buf.append(f'<h1>{self.title}</h1>')
        str_buf.append('<ul>')

        for item in self._content:
            str_buf.append(item.make_html())
        str_buf.append('</ul>')
        str_buf.append(f'<hr><adress>{self.author}</adress>')
        str_buf.append('</body></html>')
        return '\n'.join(str_buf)
list_tray.py
from factory.tray import Tray


class ListTray(Tray):
    def __init__(self, caption: str) -> None:
        super().__init__(caption)

    def make_html(self) -> str:
        str_buf: list = []

        str_buf.append('<li>')
        str_buf.append(self._caption)
        str_buf.append('<ul>')
        for item in self._tray:
            str_buf.append(item.make_html())
        str_buf.append('</ul>')
        str_buf.append('</li>')
        return '\n'.join(str_buf)

参考文献

実践python3
増補改訂版Java言語で学ぶデザインパターン入門

Discussion