0
0

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.

Abstract Factoryパターン Python実装 メモ

Posted at
  • Pythonとデザインパターンの学習記録。今回はAbstract Factoryパターンについてメモする。

Abstract Factoryパターン とは

  • インスタンスの生成を専門に行うクラスを用意し、整合性が必要とされるオブジェクト群を間違いなく生成するためのデザインパターン。
  • 以下のような場合、オブジェクトの生成を担う専用のファクトリークラスを用意し、必要な属性を組み合わせる処理をファクトリークラスに実行させる。
    • あるオブジェクト(例:エンドユーザー)は複数の属性(例:ID、名前、メールアドレス)から構成される。
    • 属性はあるオブジェクトの構成要素に属するものとして定義される。
      (例:エンドユーザーを定義するためのIDや名前はエンドユーザー用の構成要素に属する。サポートオペレーターを定義するためのIDや名前は別の構成要素に属する。)

Factory Method パターンとの違い

  • Factory Method パターン:

    • 実装を持つクラスを作成するためのファクトリーを抽象メソッドで実現する。
      • 生成対象のクラスが複数の場合、クラス内のファクトリー(factory method)が増えていく。
  • Abstract Factory パターン:

    • ファクトリー自体を他のクラスへ外部化する。
      • ファクトリーを全て外部クラスに移し、それらのクラスに処理を委ねることでクラスの肥大化を防ぐ。

サンプルプログラム

  • ユーザー取得処理を行うAPIに適用する。
    • クエリパラメータの有無でユーザー生成処理(固定 or ランダム)を切り替える。

      • 固定ユーザー生成:FixedUserFactoryFixedIDFixedNameFixedEmail
      • ランダムユーザー生成:RandomUserFactoryRandomIDRandomNameRandomEmail

      abstract_factory_code.png

ディレクトリ構成

Project
│  app.py
L_ api
   │  __init__.py
   │
   ├─views
     │  sample.py
     │  __init__.py
     │
	 ├─factory
  		│  factory.py
  		│  __init__.py
  		│
  		├─fixeduserfactory
  		│  L_  fixed_user_factory.py
  		│  L_  __init__.py
  		│
  		├─randomuserfactory
  		  L_  random_user_factory.py
  		  L_  __init__.py
  		

実装

  • app.py

    from api import app
    
    if __name__ == '__main__':
        app.run()
    
    
  • api/__init__.py

    from flask import Flask
    from .views.sample import sample_router
    
    
    def create_app():
    
        app = Flask(__name__)
    
        app.register_blueprint(sample_router, url_prefix='/api')
    
        return app
    
    
    app = create_app()
    
    
  • api/views/sample.py

    • APIコントローラ
    • クエリパラメータの有無でユーザー生成方法を切り替える。
    from flask import Flask, Blueprint, request
    import json
    from .factory.factory import AbstractUserFactory
    from .factory.fixeduserfactory.fixed_user_factory import FixedUserFactory
    from .factory.randomuserfactory.random_user_factory import RandomUserFactory
    
    # Routing Settings
    sample_router = Blueprint('sample_router', __name__)
    
    app = Flask(__name__)
    
    
    @sample_router.route("/sample",  methods=['GET'])
    def get():
    
        query = request.args.get('q')
        user = {}
        if query is not None:
            f = AbstractUserFactory(FixedUserFactory())
            user = f.make_user("@fixed.example.com")
        else:
            f = AbstractUserFactory(RandomUserFactory())
            user = f.make_user("@random.example.com")
    
        return json.loads(json.dumps(user))
    
  • api/views/factory/factory.py

    • AbstractFactoryとAbstractProduct
    # AbstractFactory
    class AbstractUserFactory:
        def __init__(self, user_factory):
            self.factroy = user_factory
    
        def make_user(self, domain):
            self.user = {}
            self.user["id"] = self.factroy.get_id()
            self.user["name"] = self.factroy.get_name()
            self.user["email"] = self.factroy.get_email(domain)
            return self.user
    
        # createproduct
        def get_id(self):
            pass
    
        # createproduct
        def get_name(self):
            pass
        
    	# createproduct
        def get_email(self, domain="@example.com"):
            pass
    
    # AbstractProduct
    class ID:
        def generate(self):
            pass
    
    
    class Name:
        def generate(self):
            pass
    
    
    class Email:
        def __init__(self, domain):
            self.domain = domain
    
        def generate(self):
            pass
    
    
  • api/views/factory/fixeduserfactory/fixed_user_factory.py

    • Concrete FactoryとConcrete Product
    • 固定のユーザー情報を生成
    from api.views.factory.factory import AbstractUserFactory, ID, Name, Email
    
    # ConcreteFactory
    class FixedUserFactory(AbstractUserFactory):
        def __init__(self):
            pass
    
        # createproduct
        def get_id(self):
            return FixedID().generate()
    
        # createproduct
        def get_name(self):
            return FixedName().generate()
    
        # createproduct
        def get_email(self, domain):
            return FixedEmail(domain).generate()
    
    
    # ConcreteProduct
    class FixedID(ID):
        def generate(self):
            id = "ABCDE12345"
            print("固定 ID:{0} を生成しました。".format(id))
            return id
    
    
    # ConcreteProduct
    class FixedName(Name):
        def generate(self):
            name = "Yamada Taro"
            print("固定 Name:{0} を生成しました。".format(name))
            return name
    
    
    # ConcreteProduct
    class FixedEmail(Email):
        def generate(self):
            email = "example"+self.domain
            print("固定 Email:{0} を生成しました。".format(email))
            return email
    
    
  • api/views/factory/randomuserfactory/random_user_factory.py

    • Concrete FactoryとConcrete Product
    • ユーザー情報をランダム生成
    from api.views.factory.factory import AbstractUserFactory, ID, Name, Email
    import random
    
    
    # ConcreteFactory
    class RandomUserFactory(AbstractUserFactory):
        def __init__(self):
            pass
    
        # createproduct
        def get_id(self):
            return RandomID().generate()
    
        # createproduct
        def get_name(self):
            return RandomName().generate()
    
        # createproduct
        def get_email(self, domain):
            return RandomEmail(domain).generate()
    
    
    # ConcreteProduct
    class RandomID(ID):
        def generate(self):
            chars = '0123456789abcdefghijklmnopqrstuvwxyz'
            id = ''.join([random.choice(chars) for _ in range(10)])
            print("ランダム ID:{0} を生成しました。".format(id))
            return id
    
    
    # ConcreteProduct
    class RandomName(Name):
        def generate(self):
            num = random.randrange(3)
            name_list = ["Yamada Taro", "Sato Jiro", "Tanaka Saburo"]
    
            name = name_list[num]
            print("ランダム Name:{0} を生成しました。".format(name))
            return name
    
    
    # ConcreteProduct
    class RandomEmail(Email):
        def generate(self):
            chars = '0123456789abcdefghijklmnopqrstuvwxyz'
            local_part = ''.join([random.choice(chars) for _ in range(10)])
            email = local_part + self.domain
            print("ランダム Email:{0} を生成しました。".format(email))
            return email
    

動作確認

  • ランダムユーザー情報生成(クエリパラメータ指定なし)

    • リクエスト

      GET /api/sample HTTP/1.1
      Host: localhost:5000
      
    • レスポンス

      {
          "email": "fsq8yjug2v@random.example.com",
          "id": "2xod95th7u",
          "name": "Sato Jiro"
      }
      
  • 固定ユーザー情報生成(クエリパラメータ指定あり)

    • リクエスト

      GET /api/sample?q=test HTTP/1.1
      Host: localhost:5000
      
    • レスポンス

      {
          "email": "example@fixed.example.com",
          "id": "ABCDE12345",
          "name": "Yamada Taro"
      }
      

参考情報

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?