web-dev-qa-db-ja.com

Javaでの一般的なファイルパーサーの設計

モジュールの1つがXMLファイルを解析し、必要なコンテンツをデータベースにダンプすることを担当している製品に取り組んでいます。現在の要件はXMLファイルを解析することだけですが、将来、あらゆる種類のファイルをサポートできるように解析モジュールを設計したいと考えています。このアプローチの理由は、特定のクライアント向けにこの製品を構築しているが、近い将来に他のクライアントに販売することを計画しているためです。現在のクライアントのエコシステム内のすべてのシステムはXMLファイルを生成および使用しますが、これは他のクライアントの場合とは異なる場合があります。

これまでに何を試しましたか?(現在)戦略パターンに基づいた次のデザインを念頭に置いています。 Eclipseでコードをすぐに書き留めて設計を伝えたので、例外を処理する適切な方法などの他の側面が今のところ無視されているとすばらしいでしょう。

Parser:解析メソッドを公開する戦略インターフェース。

 public interface Parser<T> {
        public T parse(String inputFile);
    }

*ジェネリックパラメーターを使用する理由は、戻り値の型を許可し、コンパイル時の型の安全性を確保するためです。

ProductDataXmlParser製品関連情報を含むproduct.xmlファイルを解析するための具象クラス。 (XMLBeansを使用)

public class ProductDataXmlParser implements Parser<ProductDataTYPE> {

    public ProductDataTYPE parse(String inputFile) {
        ProductDataTYPE productDataDoc = null;
            File inputXMLFile = new File(inputFile);

        try {
            productDataDoc = ProductDataDocument.Factory.parse(inputXMLFile);
        } catch(XmlException e) {
            System.out.println("XmlException while parsing file : "+inputXMLFile);
        } catch(IOException e) { 
                 System.out.println("IOException while parsing file : "+inputXMLFile);
        }
        return productDataDoc.getProductData();
    }
} 

where:ProductDataTYPEおよびProductDataDocumentは、xsdおよびscompコマンドを使用して生成されたXMlBean POJOクラスです。

未来

今後解析するproduct.txtファイルがある場合は、ファイルの必要なコンテンツを保持するProductDataという独自のPOJOを定義できます。次に、Parserインターフェイスを実装するProductDataFlatFileParserという具象クラスを作成し、ファイルを解析した後で、parseメソッドにProductData POJOを入力させます。

このデザインは意味がありますか?このデザインに明らかな欠陥はありますか?デザインが現状のままであるので、具象クラスがファイルを解析するアルゴリズムを定義できるようにし、具象クラスにデータを入力する場所を決定させます。デザインは、ファイル形式ではなくドメインオブジェクトに依存しているようです。これは悪いことですか?デザインを改善する方法についてのご意見をお待ちしております。

14
CKing

私にはいくつかの懸念があります:

  1. 実装する前に、一般的な設計が実際に必要であることを確認します。 XML以外のファイルの種類が必要になると確信していますか?そうでない場合、なぜそれらをコード化するのですか?最終的に必要になった場合は、その時点でコードを改良できます。それはそれほど長くはかからず、おそらくコードが現在提案しているものとは異なるように見える他の要件があり、おそらくとにかくそれを書く必要はないでしょう。彼らが言うように、YAGNI(あなたはそれを必要としないでしょう)。
  2. 実際に一般的な設計が必要であり、これに確信がある場合、私はParser<T>は基本的に音です。 2つの潜在的な問題が見られます。(1)ファイル入力を想定しています。たとえば、HTTP応答から取得したJSONストリームを解析しようとしている場合はどうでしょうか。 (2)大量の異なるタイプのデータ用の多数の異なるタイプのパーサーがある、いくつかのより大きな一般的なフレームワークの一部として以外は、それは必ずしも多くの価値を提供しません。しかし、そのような大規模な汎用フレームワークが必要だとは思いません。私が知る限り、あなたは今非常に単純で具体的な使用例を持っています:XMLファイルをProductDatasのリストに解析します。
  3. ProductDataXmlParserで行っているように、例外を飲み込むことはほとんど決して良い考えではありません。代わりに、ある種のRuntimeExceptionに変換します。
7
Eric Galluzzo

あなたのデザインは最良の選択肢ではありません。あなたの設計により、それを使用する唯一の方法:

ProductDataXMLTYPE parser = new ProductDataXmlParser<ProductDataXMLTYPE>().parse(input); 
ProductDataTextTYPE parser = new ProductDataTextParser<ProductDataTextTYPE >().parse(input);

上記の例ではあまりメリットがありません。次のようなことはできません。

Parser parser = getParser(string parserName);
parser.parse();

ジェネリックを探す前に、次の2つのオプションを検討できます。

  • 1、解析後の同じ出力

データソースの場所に関係なく、製品データはデータベースに保存する前と同じ形式になります。これは、クライアントとダンプサービスの間の契約です。したがって、出力と同じProductDataがあると想定します。あなたは単にインターフェースを定義することができます:

public interface Parser {
    public ProductData parse(String inputFile);
}

さらに、より柔軟にしたい場合は、ProductDataをインターフェイスとして定義します。

パーサーをデータと混合したくない場合。次の2つのインターフェイスに分割できます。

public interface Parser {
     public void parse(String inputFile);
}
public interface Data {
    public ProductData getData();
}

そして、あなたのパーサーは次のようになります:

public class XMLParser implements Parser, Data {} 
public class TextParser implements Parser, Data {}
  • 2、解析後の異なる出力

ProductDataが類似しておらず、Parserインターフェースを再利用したい場合。あなたはこの方法でそれを行うことができます:

public interface Parser {
   public void parse(String inputFile);
}

class XMLParse implements {
      @Override
      public void parse(String inputFile);

      ProductDataXML getProductData();        
}

class TextParse implements {
      @Override
      public void parse(String inputFile);

      ProductDataText getProductData();        
}
1
Canhua Li