Python設計モードの工場モード

8369 ワード

工場モード
作成型設計モードは、オブジェクト作成に関する問題を処理し、直接オブジェクトを作成する(Pythonではinit()関数で実現される)ことが不便な場合に、より良い方法を提供することを目的としています.
ファクトリ設計モードでは、クライアント1は、このオブジェクトがどこから来たのかを知る必要がなく、オブジェクトを要求することができる.すなわち,どのクラスを用いてこのオブジェクトを生成するかを知る必要はない.工場の背後にある考えは、オブジェクトの作成を簡素化することです.クライアント自身がクラスインスタンス化に基づいて直接オブジェクトを作成するよりも、中心化関数に基づいて実装する方が、作成したオブジェクトを追跡しやすくなります.オブジェクトを作成するコードとオブジェクトを使用するコードをデカップリングすることで、ファクトリはアプリケーションメンテナンスの複雑さを低減することができます.
ファクトリには通常、2つの形式があります.1つはファクトリメソッド(Factory Method)で、異なる入力パラメータに対して異なるオブジェクトを返す方法(または、本物のPython用語では関数)です.2つ目は抽象ファクトリであり、一連の関連する物事オブジェクトを作成するためのファクトリメソッドのセットです.
プラントメソッド
ファクトリメソッドモードでは、単一の関数を実行し、パラメータ(私たちが何を望んでいるかを示す情報を提供する)を入力しますが、オブジェクトがどのように実装され、オブジェクトがどこから来たのかの詳細を知る必要はありません.
1つの関数/メソッドではなく、作成オブジェクトを適用するコードが複数の異なる場所に分布しているため、これらのオブジェクトを追跡できないことが分かった場合は、ファクトリメソッドモードの使用を考慮する必要があります.ファクトリメソッドは、オブジェクトを1つの場所に集中して作成し、オブジェクトの追跡を容易にします.複数のファクトリメソッドを作成しても問題ありません.実際には、類似したオブジェクトの作成を論理的にグループ化し、各ファクトリメソッドが1つのグループを担当することが一般的です.たとえば、別のデータベース(MySQL、SQLite)に接続するファクトリメソッドと、必要なジオメトリ(円形、三角形)を作成するファクトリメソッドなどがあります.
オブジェクトの作成とデカップリングが必要な場合は、ファクトリメソッドも役立ちます.オブジェクトを作成するときは、12の特定のクラスに結合/バインドされていません.関数を呼び出すことで、私たちが何を望んでいるのかに関する一部の情報を提供します.これは、この関数を変更するのが容易であり、この関数を使用するコードを同時に変更する必要がないことを意味します.
もう1つの特筆すべきアプリケーションの例は、アプリケーションのパフォーマンスとメモリの使用に関連しています.ファクトリメソッドは、必要に応じて新しいオブジェクトを作成し、パフォーマンスとメモリ使用率を向上させることができます.クラスを直接インスタンス化してオブジェクトを作成する場合は、新しいオブジェクトを作成するたびに追加のメモリを割り当てる必要があります(このクラス内でキャッシュが使用されていない限り、一般的にはそうではありません).アクションで話すと、次のコード(ファイルid.py)は同じクラスAに2つのインスタンスを作成し、関数id()を使用してメモリアドレスを比較します.出力にもアドレスが含まれており、アドレスが正しいかどうかを確認しやすい.メモリアドレスが異なると、2つの異なるオブジェクトが作成されることを意味します.
まずgithubのコードを見てみましょう.
#   github    :
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""http://ginstrom.com/scribbles/2007/10/08/design-patterns-python-style/"""


class GreekGetter:

    """A simple localizer a la gettext"""

    def __init__(self):
        self.trans = dict(dog="σκύλος", cat="γάτα")

    def get(self, msgid):
        """We'll punt if we don't have a translation"""
        return self.trans.get(msgid, str(msgid))

class EnglishGetter:

    """Simply echoes the msg ids"""

    def get(self, msgid):
        return str(msgid)

def get_localizer(language="English"):
    """The factory method"""
    languages = dict(English=EnglishGetter, Greek=GreekGetter)
    return languages[language]()

# Create our localizers
e, g = get_localizer(language="English"), get_localizer(language="Greek")
# Localize some text
for msgid in "dog parrot cat bear".split():
    print(e.get(msgid), g.get(msgid))

### OUTPUT ###
# dog σκύλος
# parrot parrot
# cat γάτα
# bear bear

関数ファクトリを介して、対応するインスタンスが返されます.入力するパラメータは、管理する必要はありません.関数は、インスタンス化が必要なクラスを自動的に見つけます.
次のように、Pythonリリースに付属する2つのライブラリ(xml.etree.ElementTreeおよびjson)を使用してXMLおよびJSONを処理します.
import xml.etree.ElementTree as etree
import json

クラスJSOnConnector解析JSONファイル、parsed_経由data()メソッドは、辞書(dict)としてデータを返します.修飾器property parsed_Data()は、次のように方法ではなく、通常の変数のように見えます.
class JSONConnector:

    def __init__(self, filepath):
        self.data = dict()
        #            
        # with open(filepath, mode='r', encoding='utf-8') as f: 
        with open(filepath, mode='r') as f:
            self.data = json.load(f)

    @property
    def parsed_data(self):
        return self.data

クラスXMLConnectorはXMLファイルを解析し、parsed_を介してdata()メソッドはxmlである.etree.Elementリストの形式では、次のようにすべてのデータが返されます.
class XMLConnector:

    def __init__(self, filepath):
        self.tree = etree.parse(filepath)

    @property
    def parsed_data(self):
        return self.tree

関数connection_factoryは、入力ファイルパスの拡張子に基づいてJSOONConnectorまたはXMLConnectorのインスタンスを返すファクトリメソッドです.以下に示します.
def connection_factory(filepath):
    if filepath.endswith('json'):
        connector = JSONConnector
    elif filepath.endswith('xml'):
        connector = XMLConnector
    else:
        raise ValueError('Cannot connect to {}'.format(filepath))
    return connector(filepath)

関数connect_to()対connection_factory()は包装を行い、以下に示す異常処理を追加した.
def connect_to(filepath):
    factory = None
    try:
        factory = connection_factory(filepath)
    except ValueError as ve:
        print(ve)
    return factory

ファクトリメソッドを使用してXMLファイルを処理する方法を示します.XPathは、姓(last name)がLiarであるperson要素をすべて検索するために使用されます.一致する各要素について、以下に示すように、その基本的な名前と電話番号情報が表示されます.
xml_factory = connect_to('data/person.xml')
xml_data = xml_factory.parsed_data
liars = xml_data.findall(".//{}[{}='{}']".format('person', 'lastName', 'Liar'))
print('found: {} persons'.format(len(liars)))
for liar in liars:
    print('first name: {}'.format(liar.find('firstName').text))
    print('last name: {}'.format(liar.find('lastName').text))
    [print('phone number ({})'.format(p.attrib['type']), p.text) for p in liar.find('phoneNumbers')]

最後のセクションでは、工場メソッドを使用してJSONファイルを処理する方法を示します.ここではパターンマッチングがないので、以下に示すように、すべてのドーナツのname、price、topping.
json_factory = connect_to('data/donut.json')
json_data = json_factory.parsed_data
print('found: {} donuts'.format(len(json_data)))
for donut in json_data:
    print('name: {}'.format(donut['name']))
    print('price: ${}'.format(donut['ppu']))
    [print('topping: {} {}'.format(t['id'], t['type'])) for t in donut['topping']]

完全なコードは次のとおりです.
import xml.etree.ElementTree as etree
import json

class JSONConnector:

    def __init__(self, filepath):
        self.data = dict()
        #            
        # with open(filepath, mode='r', encoding='utf-8') as f: 
        with open(filepath, mode='r') as f:
            self.data = json.load(f)

    @property
    def parsed_data(self):
        return self.data

class XMLConnector:

    def __init__(self, filepath):
        self.tree = etree.parse(filepath)

    @property
    def parsed_data(self):
        return self.tree

def connection_factory(filepath):
    if filepath.endswith('json'):
        connector = JSONConnector
    elif filepath.endswith('xml'):
        connector = XMLConnector
    else:
        raise ValueError('Cannot connect to {}'.format(filepath))
    return connector(filepath)

def connect_to(filepath):
    factory = None
    try:
        factory = connection_factory(filepath)
    except ValueError as ve:
        print(ve)
    return factory


sqlite_factory = connect_to('data/person.sq3')
print()
xml_factory = connect_to('data/person.xml')
xml_data = xml_factory.parsed_data
liars = xml_data.findall(".//{}[{}='{}']".format('person', 'lastName', 'Liar'))
print('found: {} persons'.format(len(liars)))
for liar in liars:
    print('first name: {}'.format(liar.find('firstName').text))
    print('last name: {}'.format(liar.find('lastName').text))
    [print('phone number ({})'.format(p.attrib['type']), p.text) for p in liar.find('phoneNumbers')]
print()
json_factory = connect_to('data/donut.json')
json_data = json_factory.parsed_data
print('found: {} donuts'.format(len(json_data)))
for donut in json_data:
    print('name: {}'.format(donut['name']))
    print('price: ${}'.format(donut['ppu']))
    [print('topping: {} {}'.format(t['id'], t['type'])) for t in donut['topping']]

JSOnConnectorとXMLConnectorは同じインタフェースを持っていますが、parsed_data()が返すデータは,統一的に処理されるわけではない.コネクタごとに異なるPythonコードを使用して処理する必要があります.すべてのコネクタに同じコードを適用できるのは当然望ましいが、データに共通のマッピングを使用しない限り、このマッピングは通常、外部データプロバイダによって提供される場合が多い.XMLファイルとJSONファイルを同じコードで処理できると仮定しても、第3のフォーマット(例えばSQLite)をサポートする必要がある場合、コードにどのような変更を加えるべきでしょうか.SQliteファイルを探したり、自分で作成したりして、試してみてください.
接続のみでfactoryにsqliteのファイルフォーマットを追加し、sql操作のクラスを追加すればいいです.
コードはコネクタを直接インスタンス化することを禁止していません.直接インスタンス化を禁止する場合は、実装できますか?
はい、Pythonではclassにclassをネストできます.