web-dev-qa-db-ja.com

さまざまなソースタイプのデータをさまざまな宛先タイプにインポートするためのデザインパターン

以下を処理できるインポートスクリプト(C#で)を設計およびビルドする必要があります。

  • さまざまなソース(XML、XSLX、CSV)からデータを読み取る
  • データを確認する
  • さまざまなオブジェクトタイプ(顧客、住所)にデータを書き込む

データは多数のソースから取得されますが、ソースには常に1つのインポート形式(csv、xml、xslx)があります。インポート形式はソースごとに異なります。新しいインポート形式が将来追加される可能性があります。宛先オブジェクトのタイプは常に同じです(顧客、アドレスなど)。

私はジェネリックの使用を考えていて、ファクトリーパターンについて何かを読みましたが、私はこの分野ではかなりの初心者なので、アドバイスは大歓迎です。

この問題を解決するための適切な設計パターンは何ですか?

14
jao

あなたは空想のコンセプトで船外に行く予定が早すぎました。ジェネリックス-ケースを見た場合はジェネリックを使用しますが、それ以外の場合は心配する必要はありません。ファクトリー・パターン-これには柔軟性がありすぎ(そして混乱が加わりました)ます。

複雑にしないでおく。基本的な方法を使用します。

  1. XMLの読み取りと、CSVの読み取りを行うことの間の一般的なことを想像してみてください。次のレコード、次の行など。新しいフォーマットが追加される可能性があるため、決定されるフォーマットと既知のフォーマットとの共通性を想像してみてください。この共通性を使用して、すべてのフォーマットが準拠しなければならない「インターフェース」または契約を定義します。それらは共通の根拠に固執しますが、それらはすべて特定の内部ルールを持っている可能性があります。

  2. データを検証するために、新しいまたは異なる検証コードブロックを簡単にプラグインする方法を提供するようにしてください。そのため、特定の種類のデータ構成を担当する各バリデーターがコントラクトに準拠するインターフェースを定義してみてください。

  3. データ構造を作成する場合、提案された出力オブジェクトを何よりも設計する人によって制約を受ける可能性があります。データオブジェクトの次のステップが何であるかを理解し、最終的な使用方法を知ることによって最適化できるかどうかを確認してください。たとえば、オブジェクトがインタラクティブなアプリケーションで使用されることがわかっている場合、オブジェクトの「合計」またはカウント、または他の種類の派生情報を提供することで、そのアプリの開発者を支援できます。

これらのほとんどはテンプレートパターンまたは戦略パターンです。プロジェクト全体がアダプタパターンになります。

11
Andyz Smith

明らかなことは Strategy pattern を適用することです。汎用基本クラスReadStrategyを使用し、入力形式ごとにXmlReadStrategyCSVReadStrategyなどのサブクラスを使用します。これにより、検証処理や出力処理。

詳細によっては、インポートジェネリックのほとんどの部分を保持し、入力処理の一部のみを交換することも可能です(たとえば、1つのレコードの読み取り)。これは テンプレートメソッドパターン につながる可能性があります。

9
Doc Brown

将来拡張する必要があるインポートユーティリティの適切なパターンは、MEFを使用することです-レイジーリストから必要なコンバーターをオンザフライで読み込み、属性で装飾されたMEFインポートを作成することにより、メモリ使用量を低く抑えることができます。これにより、実行しようとしているインポートに適したコンバーターを選択でき、さまざまなインポートクラスを簡単に分離できます。

各MEFパーツは、インポートファイルの行を出力データに変換するか、基本機能で基本クラスをオーバーライドするいくつかの標準メソッドでインポートインターフェイスを満たすように構築できます。

MEFはプラグインアーキテクチャを作成するためのフレームワークです。OutlookとVisual Studioがどのように構築されるか、VSのこれらの素敵な拡張機能はすべてMEFパーツです。

MEF(Managed Extensability Framework)アプリを構築するには、System.ComponentModel.Compositionへの参照を含めることから始めます。

コンバーターが何をするかを特定するためのインターフェースを定義する

public interface IImportConverter
{
    int UserId { set; }        
    bool Validate(byte[] fileData, string fileName, ImportType importType);
    ImportResult ImportData(byte[] fileData, string fileName, ImportType importType);
}

これは、インポートするすべてのファイルタイプに使用できます。

クラスが何を「エクスポート」するかを定義する属性を新しいクラスに追加します

[Export(typeof(IImportConverter))]
[MyImport(ImportType.Address, ImportFileType.CSV, "4eca4a5f-74e0")]
public class ImportCSVFormat1 : ImportCSV, IImportConverter
{
 ...interface methods...
}

これは、CSVファイル(特定の形式:Format1)をインポートし、MEFエクスポート属性メタデータを設定するカスタム属性を持つクラスを定義します。インポートする形式またはファイルの種類ごとにこれを繰り返します。次のようなクラスでカスタム属性を設定できます。

[MetadataAttribute]
[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class ImportAttribute : ExportAttribute
{
    public ImportAttribute(ImportType importType, ImportFileType fileType, string customerUID)
        : base(typeof(IImportConverter))
    {
        ImportType = importType;
        FileType = fileType;
        CustomerUID = customerUID;
    }

    public ImportType ImportType { get; set; }
    public ImportFileType FileType { get; set; }
    public string CustomerUID { get; set; }
}

MEFコンバーターを実際に使用するには、変換コードの実行時に作成したMEFパーツをインポートする必要があります。

[ImportMany(AllowRecomposition = true)]
protected internal Lazy<IImportConverter, IImportMetadata>[] converters { get; set; }
AggregateCatalog catalog = new AggregateCatalog();

catalogは、フォルダーからパーツを収集します。デフォルトはアプリの場所です。

convertersは、インポートされたMEFパーツの遅延リストです

次に、変換するファイルの種類がわかったら(importFileTypeおよびimportType)、convertersのインポートされたパーツのリストからコンバーターを取得します

var tmpConverter = (from x in converters
                    where x.Metadata.FileType == importFileType
                    && x.Metadata.ImportType == importType 
                    && (x.Metadata.CustomerUID == import.ImportDataCustomer.CustomerUID)
                    select x).OrderByDescending(x => x.Metadata.CustomerUID).FirstOrDefault();

if (tmpConverter != null)
{
     var converter = (IImportConverter)tmpConverter.Value;
     result = converter.ImportData(import.ImportDataFile, import.ImportDataFileName, importType);
....
}

converter.ImportDataの呼び出しは、インポートされたクラスのコードを使用します。

たくさんのコードのように見えるかもしれませんが、何が起こっているのかを理解するのに時間がかかる場合がありますが、新しいコンバータータイプを追加する場合は非常に柔軟で、実行時に新しいものを追加することもできます。

7
Matt

この問題を解決するための適切な設計パターンは何ですか?

C#のイディオムでは、組み込みのシリアル化フレームワークを使用してこれを行います。オブジェクトにメタデータで注釈を付けてから、それらの注釈を使用するさまざまなシリアライザーをインスタンス化して、データを取り除き、正しい形式にするか、またはその逆を行います。

XML、JSON、およびバイナリフォームが最も一般的ですが、他のユーザーがすでにニースパッケージフォームに存在していても、それを利用できるとしても驚くことではありません。

0
Telastyn