Pythonでデザインパターン 肆ノ型「FactoryMethod」
今回はPythonの勉強しながらデザインパターンのFactoryMethodをやってみました。
他のパターンにつきましては、以下の目次記事をご参照ください。
FactoryMethodとは
直訳すると「工場手続き」みたいな意味になります。
wikiには「他のクラスのコンストラクタをサブクラスで上書き可能な自分のメソッドに置き換えることで、 アプリケーションに特化したオブジェクトの生成をサブクラスに追い出し、クラスの再利用性を高める」とあります。
FactoryMethodについて考えてみる
wikiの文言だと私にはよくわからなかったので、
いつものごとく色々コードを書いて自分なりの理解を書いてみたいと思います(;^ω^)
まず以下のようなProductインタフェースとそれを実装したProductA クラスとProductB クラスがあったとします。
class Product(metaclass=ABCMeta) : @abstractmethod def functionA(self) : pass class ProductA(Product) : def functionA(self) : # 機能Aを実装(処理内容は割愛) class ProductB(Product) : def functionA(self) : # 機能Aを実装(処理内容は割愛)
これを使ったmainは以下のようなイメージになります。
このときProductAもProductBもインスタンスを作成後に、それぞれの専用初期化メソッドInitializeA、B、Cを順番に呼び出さなければならないものとします。
そうするとmainにProductA、ProductBに対するそれぞれの初期化メソッドを実装しなければならなくなります。
またProductのインスタンスを作成するたびに、これらのメソッドを選択して順番に呼ばなければならなくなります。
これではProductCとかProductDとかできるたびにmainがどんどん肥大化して、複雑になっていきますね。。。(´ε`;)
def ProductAInitializeA(product : Product) : # ProductAの初期化処理A def ProductAInitializeB(product : Product) : # ProductAの初期化処理B def ProductAInitializeC(product : Product) : # ProductAの初期化処理C def ProductBInitializeA(product : Product) : # ProductBの初期化処理A def ProductBInitializeB(product : Product) : # ProductBの初期化処理B def ProductBInitializeC(product : Product) : # ProductBの初期化処理C if __name__ == '__main__' : product = ProductA() ProductAInitializeA(product) ProductAInitializeB(product) ProductAInitializeC(product) product.functionA() #これでProductAが使えるようになる product = ProductB() ProductBInitializeA(product) ProductBInitializeB(product) ProductBInitializeC(product) product.functionA() #これでProductBが使えるようになる
これを回避するもっとも単純な方法は、ProductAクラス、ProductBクラスそれぞれに専用の初期化メソッドを実装し、コンストラクタなどで初期化処理を呼び出すことが考えられます。
こうするとmainはとてもシンプルになりますね(・`ω´・)
しかし、この方法ではProductAクラス、ProductBクラスに直接手を加えなければなりません。
もし「InitializeA、B、Cがこのアプリケーションのみで有効な初期化処理だった場合」 かつ 「ProductAクラスやProductBクラスを他のシステムで使いまわしたい場合」を考えると
InitializeA、B、Cを他のシステムに合わせて変更しなければならなくなるかもしれません。
そうするとProductAクラス、ProductBクラスの再利用性は低下してしまいますね。。(;゚Д゚)
class ProductA(Product) : def functionA(self) : # 処理A def __InitializeA(self) : # 初期化処理A def __InitializeB(self) : # 初期化処理B def __InitializeC(self) : # 初期化処理C def __init__(self) : self.__InitializeA() self.__InitializeB() self.__InitializeC() class ProductB(Product) : def functionA(self) : # 処理A def __InitializeA(self) : # 初期化処理A def __InitializeB(self) : # 初期化処理B def __InitializeC(self) : # 初期化処理C def __init__(self) : self.__InitializeA() self.__InitializeB() self.__InitializeC() if __name__ == '__main__' : product = ProductA() product.functionA() product = ProductB() product.functionA()
そこで以下のようにしてみました(・`ω´・)b
この例では、まずFactoryという抽象クラスを用意しています。
このクラスには以下のものがあります。
- 初期化処理を実装するためのInitializeA、B、Cインタフェース
- Productのインスタンスを生成するためのcreateインスタンス
- 初期化手順を実装したcreateProduct()メソッド
そしてFactoryAとFactoryBは、Factoryを継承して、それぞれ以下の実装が作れらています。
- InitializeA、B、Cインタフェースを使ってProduct固有の初期化処理を実装
- createインタフェースを使ってProductのインスタンスを生成・返却するように実装
これによりFactoryのcreateProductメソッドは、createでインスタンスを取得し、InitializeA、B、Cで初期化したインスタンスを返すことができるようになります。
これで、wikiにあるように「mainからProductの生成と初期化をFactoryクラスに追い出し」、そして「Productに手を加えないため再利用性を高めることができる」
ということが実現できそうな気がします。
class Factory(metaclass=ABCMeta) : @abstractmethod def _create(self) -> Product : pass @abstractmethod def _InitializeA(self, product : Product) : pass @abstractmethod def _InitializeB(self, product : Product) : pass @abstractmethod def _InitializeC(self, product : Product) : pass def createProduct(self) : product = self._create() self._InitializeA(product) self._InitializeB(product) self._InitializeC(product) return product class FactoryA(Factory) : @abstractmethod def _create(self) -> Product : return FactoryA() @abstractmethod def _InitializeA(self, product : Product) : # 初期化処理A @abstractmethod def _InitializeB(self, product : Product) : # 初期化処理B @abstractmethod def _InitializeC(self, product : Product) : # 初期化処理C class FactoryB(Factory) : @abstractmethod def _create(self) -> Product : return FactoryB() @abstractmethod def _InitializeA(self, product : Product) : # 初期化処理A @abstractmethod def _InitializeB(self, product : Product) : # 初期化処理B @abstractmethod def _InitializeC(self, product : Product) : # 初期化処理C if __name__ == '__main__' : factory = FactoryA() product = factory.createProduct() product.functionA() factory = FactoryB() product = factory.createProduct() product.functionA()
こんな感じなのがFactoryMethodパターンかなと思っています\(^ω^)/
サンプルプログラム
以下、PythonでFactoryMethodパターンのサンプルプログラムです。
# coding: utf-8 from abc import ABC, ABCMeta, abstractmethod class Product(metaclass=ABCMeta) : @abstractmethod def use(self) -> None: pass class Account(Product) : def __init__(self, owner: str) -> None : print("Create account: ", owner) self.__owner = owner def use(self) -> None: print("Use account: ", self.__owner) def getOwner(self) -> str : return self.__owner class Factory(metaclass=ABCMeta) : def create(self, owner: str) -> Account : product = self.createProduct(owner) self.registerProduct(product) return product @abstractmethod def createProduct(self, owner: str) -> Account : pass @abstractmethod def registerProduct(self, product: Account) -> None : pass class AccountFactory(Factory) : def __init__(self) : self.__owners: list[str] = [] def createProduct(self, owner) -> Account : return Account(owner) def registerProduct(self, product: Account) -> None : self.__owners.append(product.getOwner()) def getOwners(self) -> list : return self.__owners if __name__ == '__main__' : factory = AccountFactory() account1 = factory.create("AAA") account2 = factory.create("BBB") account3 = factory.create("CCC") account4 = factory.create("DDD") account1.use() account2.use() account3.use() account4.use()
以下は実行結果です!
$ python FactoryMethod.py Create account: AAA Create account: BBB Create account: CCC Create account: DDD Use account: AAA Use account: BBB Use account: CCC Use account: DDD
- 価格: 2948 円
- 楽天で詳細を見る