Update 2023.11.18 2017.05.05

Python デザインパターン サンプルコード Factory Method
結城 浩「Java言語で学ぶデザインパターン入門」をPython化
Python3(3.11)で動くソースコード(.pyファイル .ipynbファイル)あります
「anaconda3」on .py「PyCharm」.ipynb「Jupyter Notebook」

◆◆Factory Method パターンの使われる場面◆◆

野球は野球ボール,サッカーはサッカーボール,バスケットはバスケットボールであるが,「ボールを作る」「ボールを使う」というメソッドを共通にして「競技」を使い分けられるようにするのがFactory Method パターンです。

Javaのジェネリクスと勘違いしやすいが真逆のことをしているので注意する。


(2023-11-18)Python3.11で動作確認済み


◆◆Factory Method パターンとは◆◆

GoFによれば,Factory Method パターンの目的は, 「オブジェクトを生成するときのインタフェースだけを規定して,実際にどのクラスをインスタンス化するかはサブクラスが決めるようにする。Factory Method パターンは,インスタンス化をサブクラスに任せる。 」

GoFによれば,Factory Method パターンの別名は,Virtual Constructor パターンだそうだ。

GoFによれば,Factory Method パターンは,次のような場合に用いることができる。
・クラスが,生成しなければならないオブジェクトのクラスを事前に知ることができない場合。
・サブクラス化により,生成するオブジェクトを特定化する場合。
・クラスが責任をいくつかのサブクラスの中の1つに委譲するときに,どのサブクラスに委譲するのかに関する知識を局所化したい場合。

GoFによれば,Factory Method パターンの関連するパータンは次のようなものである。
Abstract Factory パターン: factory method を使って実装されることが多い。Abstract Factory パターンの「動機」の節の例では,Factory Method パターンについても説明している。
Template Method パターン: factory method は通常,template method の中で呼ばれる。Document クラスの例では,NewDocument オペレーションが template method である。
Prototype パターン: Prototype パターンにより,Creator クラスをサブクラス化する必要はなくなるが,代わりに Product クラスにしばしば初期化オペレーションが必要になる。一方,Factory Method パターンでは初期化オペレーションは必要ない。

◆◆Factory Method パターンのサンプルのクラスの構成◆◆

ソースコードは巻末にあり,ソースファイルはこのWebの左上隅にありダウンロードできます。

  Product      Factory
  (use)       (create, createProduct, registerProduct)
   ↑         ↑
  IDCard      IDCardFactory
  (use, getOwner) (createProduct, registerProduct, getOwners)

サンプルは,カードを作って使うというものですが,その2つメソッドのインスタンス化に特徴があります。

class Product は,テンプレートであり,抽象クラスです。
class IDCard は,IDカードを使う具象クラスです。
class Factory は,テンプレートであり,抽象クラスです。オブジェクトをつくる Factory 工場でもあります。
class IDCardFactory は,IDカードを作る具象クラスです。

◆◆Factory Method パターンの実装◆◆

オブジェクト指向プログラミングの世界では Factory と言えばオブジェクトをつくる工場のことを指します。このサンプルの Factory 工場もオブジェクトをつくりますが,闇雲につくるわけではありません。

最初にクライアント(ここではメインルーチン)でクラス IDCardFactory をインスタンス化します。直後にそのインスタンスを使ってメソッド create を呼びます。呼ばれたクラス IDCardFactory にはメソッド create はないので親クラス Factory に探しに行き,そこのメソッド create が呼ばれます。それはクラス IDCard をインスタンス化する工場なのです。

親クラス Factory は子クラス IDCardFactory の具象メソッドを呼び出し,IDカードを作ったり登録したりします。ここで同時にクラス IDCard をインスタンス化しています。

最後にクライアントはこのインスタンスを使ってクラス IDCard のメソッド use を呼び出し,IDカードを使っています。

なぜこのような遠回りをするのでしょうか。それは2つの理由があります。1つは,クラス IDCard をクライアント(ここではメインルーチン)から隠ぺいしたいためです。もう1つは,IDカードを作る具象クラスと適切にペアになるクラスはクラス IDCardFactory だけが知っているからです。
インスタンス化の対象となる具象クラスはサンプルでは1つですが,複数あって呼び分けられるような構成にしてもよいわけです。if 文を使わないでインスタンスで呼び分けることが,オブジェクト指向の重要なポイントです。

◆◆ソースコード◆◆

このWebの左上隅からダウンロードできます。

ソースファイルは1つです。
・FactoryMethod.py;Factory Method パターンのPythonサンプル

【FactoryMethod.py】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from abc import ABCMeta, abstractmethod

class Product(metaclass = ABCMeta):

    @abstractmethod
    def use(self):
        pass

class Factory(metaclass = ABCMeta):

    def create(self, owner):
        p = self.createProduct(owner)
        self.registerProduct(p)
        return p
    @abstractmethod
    def createProduct(self, owner):
        pass
    @abstractmethod
    def registerProduct(self, product):
        pass

class IDCard(Product):
    def __init__(self, owner):
        self.owner = owner
        sys.stdout.write("{0}のカードを作ります。\n".format(self.owner))
    def use(self):
        sys.stdout.write("{0}のカードを使います。\n".format(self.owner))
    def getOwner(self):
        return self.owner

class IDCardFactory(Factory):
    owners = []
    def createProduct(self, owner):
        return IDCard(owner)
    def registerProduct(self, product):
        self.owners.append(product.getOwner())
    def getOwners(self):
        return owners

def main():
    factory = IDCardFactory()
    card1 = factory.create("結城浩")
    card2 = factory.create("とむら")
    card3 = factory.create("佐藤花子")
    card1.use()
    card2.use()
    card3.use()

if __name__=='__main__':
    main()
"""標準出力
結城浩のカードを作ります。
とむらのカードを作ります。
佐藤花子のカードを作ります。
結城浩のカードを使います。
とむらのカードを使います。
佐藤花子のカードを使います。
"""

以上

トップページに戻る
inserted by FC2 system