web-dev-qa-db-ja.com

巨大なCSVファイルを処理するための設計パターン-Java

Javaでデザインパターンを学習しています。また、ディスク上の巨大なCSVファイルからプログラムにストリーミングされる膨大な数のリクエストを処理する必要がある問題にも取り組んでいます。各CSV行は1つのリクエストです、および各行の最初のフィールドはメッセージタイプを示します。7種類のメッセージがあり、それぞれ異なる方法で処理する必要があります。

コードは次のようなものです。

_class Handler {
    private CustomClass {….}
    Map<String, CustomClass> map = new HashMap<String, CustomClass>();
    Public void runFile() {
        // Read the huge CSV file with millions of records, line by line
        // for each line, get the string before the first comma (say X)
        switch (X) {
        case 1 : myMethod1(….); break;
        case 2 : myMethod2(….); break;
        case 3 : myMethod3(….); break;
        // ...
        default: // ...
        }
    }
    // Methods 1, 2, 3 declarations
}
_

注1:一部のメソッドはマップに影響し、他のメソッドは影響しません。
注2:各リクエスト(メソッド)はCSV行の異なる変数を使用し、異なるロジックを実行します。
注3:リクエスト/メソッドは接続されていません。つまり、myMethod2()は論理的にmyMethod1()に続きません。

さて、私の質問–この問題に適切な設計パターンは何ですか?変更せずにロジック全体を1つのクラス(上記のコードと同様)に保持しても問題ありませんか?

5
Shad

あなたが示すコードサンプルは単純化された例であり、実際の問題はパターンを使用するに値するようにさらに複雑であると思います。

  • CustomClassを外部クラス(*)にします。
  • 同じインターフェイスを実装する複数のプロセッサを持っている
  • CSV行の形式をキーとして識別する整数を使用して、プロセッサーのマップを用意します(xと呼びます)。
  • (対応するキーを使用して)マップからプロセッサを取得し、ラインを処理します。
  • これは 戦略パターン に似ており、アルゴリズムのファミリーを定義し、各アルゴリズムをカプセル化し、そのファミリー内でアルゴリズムを交換可能にします。

利点:柔軟性、ハンドラーの外でプロセッサーのマップを作成してコンストラクターに渡す場合、後でプロセッサーを追加でき、ハンドラーは必要ありません変更する(たとえば、新しいケースのスイッチ制御構造を追加する)。

enter image description here

(*)ハンドラー内の内部クラス/インターフェースとして、インターフェースとプロセッサー、およびカスタムクラスを使用して同じ結果を得ることができますが、ソリューションをかなり汚染します。

==> CustomClass.Java <==

public class CustomClass {}

==> IMessageProcessor.Java <==

import Java.util.Map;

public interface IMessageProcessor {
    public void processLine(Map<String, CustomClass> map, String line);     
}

==> ProcessorA.Java <==

import Java.util.Map;

public class ProcessorA implements IMessageProcessor {
    @Override
    public void processLine(Map<String, CustomClass> map, String line) {
        // TODO Auto-generated method stub
    }
}

==> ProcessorB.Java <==

import Java.util.Map;

public class ProcessorB implements IMessageProcessor {
    @Override
    public void processLine(Map<String, CustomClass> map, String line) {
        // TODO Auto-generated method stub
    }
}

==> ProcessorC.Java <==

import Java.util.Map;

public class ProcessorC implements IMessageProcessor {
    @Override
    public void processLine(Map<String, CustomClass> map, String line) {
        // TODO Auto-generated method stub
    }
}

==> Handler.Java <==

import Java.util.HashMap;
import Java.util.Map;

public class Handler {
    private Map<String, CustomClass> map = new HashMap<String, CustomClass>();
    private Map<Integer,IMessageProcessor> processors = new HashMap<Integer,IMessageProcessor>();
    public processFile(){
        // store the processors in their map with the appropiate keys 
        processors.put(1, new ProcessorA());
        processors.put(2, new ProcessorB());
        processors.put(3, new ProcessorC());

        // Read the huge CSV file with millions of records, line by line
        // for each line, get the string before the first comma (say x)
        processors.get(x).processLine(map,line);
    }
}

注:最初に、キーxのプロセッサが存在するかどうかを検証し、存在しない場合は、デフォルトのプロセッサにフォールバックし、たとえば、storedキー-1、またはCSVファイルに存在しないことが保証されているその他の値。

5

シンプルに、バカに(KISS)

この問題に適切な設計パターンは何ですか?変更せずにロジック全体を1つのクラス(上記のコードと同様)に保持しても問題ありませんか?

はい、そのクラスのロジックはすべて残してください。デザインパターンが気の利いたものであるという理由だけで、エキゾチックなデザインパターンを適用しようとしないでください。デザインパターンで達成できる唯一のことは、物事を不必要に複雑にすることです。

5
Winston Ewert