7

私のプログラムにどのように良いデザインをするかについて質問があります。私のプログラムは非常にシンプルですが、私は良いアーキテクチャーを持ち、私のプログラムを将来容易に拡張できるようにしたいと考えています。拡張可能なプログラムのデザインパターンが良い

私のプログラムでは、外部データソース(XML)からデータを取り出し、これらのデータから情報を抽出し、最終的に情報をデータベースにインポートするためにSQL文を準備する必要があります。だから、現在、そこに存在するすべての外部データソースには、アプリケーションの単純な「フロー」があります:フェッチ、抽出、ロード。

私は、DataFetcher、DataExtractor、DataLoaderという汎用クラスを作成し、それらから継承する特定のクラスを作成することを考えていました。私はいくつかの工場設計パターンが必要だろうと思いますが、それはどちらですか? FactoryMethodまたは抽象ファクトリ?私も、このようなコードを使用しないようにしたい

if data_source == 'X': 
    fetcher = XDataFetcher() 
elif data_source == 'Y': 
    fetcher = YDataFetcher() 
.... 

理想的には(私はこれが容易に可能であるかどうかわからないです)、私は新しい「データソースプロセッサ」を書きたいと思い、1または2を追加既存のコードと私のプログラムの行は、新しいデータソースからデータをロードします。

目標を達成するためにデザインパターンを使用するにはどうすればよいですか?あなたはPythonでいくつかの例を提供することができれば素晴らしいだろう。ただ、きれいな慣用的なコードを書く - 柔軟なものを維持する限り

fetcher_dict = {'X':XDataFetcher,'Y':YDataFetcher} 
data_source = ... 
fetcher = fetcher_dict[data_source]() 

:フェッチャすべて同じインタフェースを持っている場合

答えて

8

は、あなたは辞書を使用することができます。私は "あなたがそれを必要としない"(YAGNI)哲学を好む傾向があります。将来に向けて何を求めようとしているかを調べるのに時間をかけすぎると、実際に必要なものが見つかったときにコードが膨大かつ複雑になり、簡単な調整が可能になります。コードが前面にきれいになっている場合は、必要に応じて後でリファクタリングするのは簡単です。

+0

あなたはデータベースにfetcher_dictを入れることもできますので、コードを変更する必要はありません(もちろんフェッチャーコード以外) – RickyA

+0

@ mgilsonは私の心を読みます。デザインパターンに多大な労力を費やすことはありません。それらはコード設計の点で過大評価されています。特定の問題を解決するためのポインタとしてそれらを参照してください。あなたの周りにあなたのデザインを構築しないでください。さもなければ、コードの判読不能な混乱に終わるでしょう。私はそれを何度も見てきました。 – RickyA

+1

マップは偽装されたswitch文です) – atamanroman

0

あなたがしようとしているのは、モジュールの動的インポート(いくつかの基本クラスに基づいています)です。動的DLLロードのC++ユースケースとよく似ています。

SO questionを試してください。 importlib.import_module用ならびにPythonのドキュメント(__import__周りだけのラッパーである)XMLをstucturedさ

​​
+0

これをフェッチャーとデータベース用に組み合わせて(非コードソリューション)、良いプラグインシステムを持っています。コードの書き換えは不要です。再び:おそらく過剰殺人。 – RickyA

+0

またはロードしたいモジュール(基本的にはプラグイン)をcmdのライン引数(リンクされたSOの質問と同じように)にするだけで済みます。このようにして、コードを変更する必要があるのは、プラグインだけで、あなたの「呼び出し元」ではなく(インターフェースが変更されない限り、別の問題です)。 – g19fanatic

0

、SQL-インサートは、平板状です。シンプルに聞こえますが、過剰にエンジニアリングしないでください。あなたの基本的なアプローチは、おそらく次のようになります。

  1. 木の上に木や再帰を埋めるために木
  2. クエリのカップルを取り戻す、ファイルシステムにXMLの
  3. コールパーサーをXMLファイルの束を探します表形式のデータ構造
  4. の束は、挿入

のカップルを生成するデータ構造をシリアライズあなたの「ビジネスロジックは、」ケースにケースから変わるポイント3、です。よく書かれた例は、いくつかの抽象レイヤーより多くの後継者を助けるでしょう。すべてのことは、ドメイン固有の固有の言語に匹敵するには小さすぎるでしょう。また、XSLTは既に存在します。

他の点は再利用の候補ですが、私には、よく書かれ、よく文書化された機能のように聞こえます。

1

あなたは、データの最も重要な部分、つまりデータの形について話すことを怠りました。それは本当にここで最も重要なことです。 「デザインパターン」は気を散らすものです。これらのパターンの多くは、Pythonにはない言語の制限があり、不必要な剛性をもたらすために存在します。

  1. まず、データの形状を調べます。例:????
    1. まず、XMLは、XMLから抽出されたデータの一部収集(簡易辞書ネストされた辞書データはあなたが必要なのですか何が、それは均質または不均質です。これは、最も重要なその後
    2. あなたが持っている
    3. 持っていますあなたはそれについて話しません!)
    4. 次に、このデータをSQLバックエンドでシリアル化/永続化します。
  2. 次に、そのデータの操作を容易にするために、メソッド、プロパティ、またはdictまたはtuple内の単なる項目の「インターフェース」(口頭での説明)を設計します。シンプルなままにして、ネイティブのPython型に固執すれば、クラス、関数、ディクテーション/タプルは必要ないかもしれません。
  3. アプリケーションに必要な抽象レベルが得られるまで、反復処理を繰り返します。

たとえば、「抽出者」のインターフェイスは「xml文字列を生成する反復可能」である可能性があります。これは、__iter__next()メソッドを持つクラスまたはクラスのいずれかになります。抽象クラスを定義し、それをサブクラス化する必要はありません!

データに追加できる構成可能なポリモーフィズムの種類は、データの正確な形状によって異なります。たとえば、あなたは規則を使用することができます。

# persisters.py 

def persist_foo(data): 
    pass 

# main.py 
import persisters 

data = {'type':'foo', 'values':{'field1':'a','field2':[1,2]}} 
try: 
    foo_persister = getitem(persisters, 'persist_'+data['type']) 
except AttributeError: 
    # no 'foo' persister is available! 

それとも、(多分あなたはあなたがコントロールすることはできません新しいモジュールを追加する必要があります)、さらに抽象化が必要な場合、あなたは(あるだけのdict)レジストリおよびモジュールを使用することができます大会:

# registry.py 
def register(registry, method, type_): 
    """Returns a decorator that registers a callable in a registry for the method and type""" 
    def register_decorator(callable_): 
     registry.setdefault(method, {})[type_] = callable_ 
     return callable_ 
    return register_decorator 

def merge_registries(r1, r2): 
    for method, type_ in r2.iteritems(): 
     r1.setdefault(method, {}).update(r2[method]) 

def get_callable(registry, method, type_): 
    try: 
     callable_ = registry[method][type] 
    except KeyError, e: 
     e.message = 'No {} method for type {} in registry'.format(method, type) 
     raise e 
    return callable_ 

def retrieve_registry(module): 
    try: 
     return module.get_registry() 
    except AttributeError: 
     return {} 

def add_module_registry(yourregistry, *modules) 
    for module in modules: 
     merge_registries(yourregistry, module) 

# extractors.py 
from registry import register 

_REGISTRY = {} 

def get_registry(): 
    return _REGISTRY 


@register(_REGISTRY, 'extract', 'foo') 
def foo_extractor(abc): 
    print 'extracting_foo' 

# main.py 

import extractors, registry 

my_registry = {} 
registry.add_module_registry(my_registry, extractors) 

foo_extracter = registry.get_callable(my_registry, 'extract', 'foo') 

したい場合(あなたはそれが少し不便だ場合でも、グローバルな状態を避けるべきであるが。)あなたは簡単にこのような構造の上にグローバルなレジストリを構築することができます

をあなたが公共のフレームワークを構築している場合最大限の拡張性と形式性が必要です。あなたは複雑さを支払うことを喜んで、あなたはzope.interfaceのようなものを見ることができます。 (これはPyramidで使用されています)

独自の抽出変換ロードアプリケーションをロールバックするのではなく、scrapyと考えましたか? scrapyを使用すると、文字列が与えられ、アイテム(データ)またはリクエスト(フェッチするURLなど、より多くの文字列のリクエスト)のシーケンスを返す「Spider」を作成します。アイテムは、それが渡される前に受信するアイテム(例えば、DBに保持されているアイテム)で何をしても構わない、構成可能なアイテムパイプラインを介して送信される。

Scrapyを使用しない場合でも、データ中心のパイプライン型の設計を採用し、具体的な「クラス」と「パターン」の代わりに抽象的な「呼び出し可能」と「反復可能」なインタフェースを考えてください。

0

私は個々の外部ソースではなくその上に抽象化を追加します。外部ソースとの対話方法を抽象化します。たとえば、SoapClient、HttpClient、XmlServiceCLient、NativeOBjectCLientなどです。この方法では、外部ソースを呼び出す新しい方法を使用する必要がある場合にのみ、新しいクラスを追加する必要があります。この方法では、新しいフェッチャクラスを頻繁に書く必要はありません。 (注:Python開発者ではありません)

外部リソースを呼び出すには、ServiceRegistryパターンを使用してください。クラスは、外部ソースを取得する必要がある場合、あなたのサービスregistry.xmlの内の一つのエントリが

<service registry> 
     <externaldatasource> 
     <dsname> xmlservice</dsname> 
     <endpointurl> some url </endpointurl> 
     <invocationtype> Soap </invocationtype> 
     </externaldatasource> 
    </service registry> 

なり、taht calssは単にtehのサービスレジストリクラスにデータソース名を渡します。 SRはxmlファイルを読み取り、外部ソースを呼び出してデータを取得します。今では、単一のクラスがすべての外部呼び出しを処理し、コードオーバーヘッドはあまりありません。

クライアントからの生データを取得したら、データモデルに変換します。私はあなた自身のXsdを持っていると仮定しています。 xsltを使用して、受信XMLをur xsd形式に変換し、検証することもできます。私は、JsonのようなXML以外のデータフォーマットを処理するためのファクトリパターンを提案します。それらを実装する必要はありません...将来の拡張のためのちょうどオープニング。

このクラス全体をゲートウェイパッケージにすることができます。外部クライアントに依存するコードはこのパッケージ内にあり、他のパッケージには感染しません。これはゲートウェイパターンと呼ばれます。このパッケージのパブリッククラスへの入出力は、あなたのドメインモデルになります。

次に、外部データソースに依存しないデータベースにロードするロジックが1つあります。

関連する問題