Factory Methodパターン

本記事はGoFのデザインパターンの1つであるFactory Methodパターンについての解説と実際に実装した記事になります。 ソースコード

適応可能性

  • どのオブジェクトをインスタンス化するかが事前に分からない場合

解決策

インスタンスを生成するメソッドを定義し、そのクラスの具象クラスにどのインスタンスを生成するかを実装させます。
これによって生成しなければならないインスタンスの種類が増えても、 このクラスの具象クラスを増やすだけですむことができます。

コード例

ユーザのIDと名前を出力するスクリプトを例にあげます。
またどの形式で出力するかをスクリプト実行時に渡します。

まず、htmlとcsv形式でユーザ情報とファイルパスを受け取って出力できるようなクラスを実装します。


class HTML(File):

    def __str__(self):
        return 'HTML Class'

    def write(self, users, file_path):
        body = self.__create_body(users)

        html = f"""
        <html>
        <head><title>Factory Method pattern</title></head>
        <body>{body}</body>
        </html>
        """
        with open(file_path, 'wt') as f:
            f.write(html)

    def __create_body(self, users):
        body = """
        <table>
            <tr>
                <th>ID</th>
                <th>name</th>
            </tr>
        """
        for user_id, name in users:
            tr = f"""
            <tr>
                <td>{user_id}</td>
                <td>{name}</td>
            </tr>
            """
            body += tr
        body += """
        </table>
        """
        return body


class CSV(File):

    def __str__(self):
        return 'CSV Class'

    def write(self, users, file_path):
        text = [['ID', 'name']]
        for user_id, name in users:
            user = [user_id, name]
            text.append(user)

        with open(file_path, 'wt') as f:
            csv_out = csv.writer(f)
            csv_out.writerows(text)

次にCreatorクラスを実装します。 こちらはFileオブジェクトをprintで出力した後にインスタンスを返したいとします。

ただし、どのインスタンスを返すかはまだ定まっていないので、 abstractmethodで定義し、具象クラスで実装してもらいます。


class FileCreator:

    @classmethod
    def create(cls):
        file = cls.create_file()
        print(f'created {file}')
        return file

    @classmethod
    @abstractmethod
    def create_file(cls):
        pass


class HTMLCreator(FileCreator):

    @classmethod
    def create_file(cls):
        return HTML()


class CSVCreator(FileCreator):

    @classmethod
    def create_file(cls):
        return CSV()

クライアントはコマンドプロントから受け取ったファイルタイプ毎に Creatorをインスタンス化し、さらにそのCreatorからFileオブジェクトを生成します。


if __name__ == '__main__':
    file_type = input('please input file type: ')

    if file_type == 'csv':
        file_path = 'test.csv'
        creator = CSVCreator()
    else:
        file_path = 'test.html'
        creator = HTMLCreator()

    users = [
        ('001', 'Taro'),
        ('002', 'Jiro')
    ]
    file = creator.create()
    file.write(users, file_path)

>>> python main.py
please input file type: csv
created CSV Class

>>> python main.py
please input file type: html
created HTML Class

注意点

別のFileオブジェクトをクライアントが使いたい場合はCreatorクラスの具象クラスを増やす必要があります。