Conception d'un analyseur de fichiers générique en Java à l'aide du modèle de stratégie

14

Je travaille sur un produit dans lequel la responsabilité de l'un des modules est d'analyser les fichiers XML et de vider le contenu requis dans une base de données. Même si la présente exigence consiste uniquement à analyser des fichiers XML, je souhaite concevoir mon module d'analyse de manière à pouvoir prendre en charge tout type de fichiers à l'avenir. La raison de cette approche est que nous construisons ce produit pour un client spécifique mais prévoyons de le vendre à d'autres clients dans un proche avenir. Tous les systèmes de l'écosystème pour le client actuel produisent et consomment des fichiers XML mais cela peut ne pas être le cas pour d'autres clients.

Qu'est-ce que j'ai essayé jusqu'à présent? (Le présent) J'ai à l'esprit la conception suivante qui est basée sur le modèle de stratégie. J'ai rapidement écrit le code dans eclipse pour transmettre ma conception, donc ce serait génial si d'autres aspects tels que la bonne façon de gérer les exceptions étaient ignorés pour l'instant.

Analyseur: interface de stratégie qui expose une méthode d'analyse.

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

* La raison d'utiliser un paramètre générique est d'autoriser tout type de retour et d'assurer la sécurité du type au moment de la compilation.

ProductDataXmlParser Une classe concrète pour analyser un fichier product.xml qui contient des informations relatives au produit. (en utilisant 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();
    }
} 

: ProductDataTYPE et ProductDataDocument sont des classes POJO XMlBean générées à l'aide d'un xsd et de la commande scomp.

L'avenir

Si j'ai un fichier product.txt à analyser à l'avenir, je peux définir mon propre POJO appelé ProductData qui contiendra le contenu requis du fichier. Je peux ensuite créer une classe concrète appelée ProductDataFlatFileParser qui implémente l'interface Parser et demander à la méthode d'analyse de remplir le ProductData POJO pour moi après avoir analysé le fichier.

Cette conception a-t-elle un sens? Y a-t-il des défauts évidents dans cette conception? Dans l'état actuel de la conception, j'autorise les classes concrètes à définir l'algorithme pour analyser un fichier et je laisse la classe concrète décider où remplir les données. La conception semble être plus dépendante des objets de domaine plutôt que des formats de fichiers. Est-ce une mauvaise chose? Toute contribution sur la façon dont je peux améliorer ma conception sera très appréciée.

CKing
la source
Le logiciel ne doit-il pas informer l'appelant des formats de fichiers pris en charge? Comment votre logiciel sait-il quel analyseur invoquer?
tomdemuyt
Vous recherchez des commentaires sur votre conception , pas sur votre implémentation réelle , donc cela sera migré vers les programmeurs où il est sur le sujet.
codesparkle
@tomdemuyt Pensez modèle d'usine;)
CKing
2
@bot L'utilisateur SO qui vous a dit de publier ceci sur Code Review avait manifestement tort. Vous auriez pu lire la FAQ du site avant de le poster, "quelqu'un m'a dit de le faire" n'est pas vraiment une bonne raison pour vous de faire quoi que ce soit. Il n'y a personne jouer au ping - pong avec le, quelqu'un a offert leur temps et a essayé de trouver un meilleur endroit pour elle au lieu de fermer purement et simplement ( ce qui aurait avait été une option valable, car il est hors sujet pour la révision du Code).
yannis
2
S'il vous plaît, ne transposez pas non plus. Vous faites un gâchis que nous devons nettoyer.
Déchiré le

Réponses:

7

J'ai quelques inquiétudes:

  1. Je m'assurerais que vous ayez réellement besoin d'une conception générique avant de l'implémenter. Voulez-vous vraiment avoir besoin de types de fichiers autres que XML? Sinon, pourquoi coder pour eux? Si vous en avez finalement besoin, vous pouvez adapter votre code à ce stade. Cela ne prendra pas beaucoup plus de temps, vous aurez probablement d'autres exigences qui rendront le code différent de ce que vous proposez actuellement, et vous n'aurez probablement jamais besoin de l'écrire de toute façon. Comme on dit, YAGNI (vous n'en aurez pas besoin).
  2. Si vous avez réellement besoin d'une conception générique et que vous en êtes sûr, je dirais que Parser<T>c'est fondamentalement sain. Je vois deux problèmes potentiels: (1) il suppose une entrée de fichier - et si vous essayez d'analyser un flux JSON que vous avez récupéré d'une réponse HTTP, par exemple? et (2) il ne fournit pas nécessairement beaucoup de valeur, sauf dans le cadre d'un cadre générique plus large où vous avez beaucoup de différents types d'analyseurs pour de nombreux types de données différents. Mais je ne suis pas convaincu que vous ayez besoin d'un tel grand cadre générique. Vous avez juste un cas d'utilisation très simple et concret pour autant que je sache: analyser un fichier XML dans une liste de ProductDatas.
  3. Ce n'est presque jamais une bonne idée d'avaler des exceptions pendant que vous le faites ProductDataXmlParser. Je le convertirais plutôt en une sorte RuntimeException.

la source
1
Nous construisons un produit qui communiquera avec de nombreux systèmes externes, donc je suppose que ce serait une bonne idée de prendre en compte tout type de format de fichier / d'entrée. Excellent point sur le flux JSON. C'est exactement pourquoi j'ai demandé à ma méthode d'analyse dans l'interface Parser de prendre un paramètre String au lieu d'un paramètre File. J'ai eu une petite erreur dans mon ProductDataXmlParser que j'ai corrigée (Besoin de passer un fichier à l'analyseur XmlBean). Vous avez également raison d'avaler des exceptions. J'ai écrit ce code rapidement dans eclipse pour transmettre ma conception sur stackoverflow à travers un exemple;)
CKing
OK cool. Je suppose que je ferais du paramètre Parser un InputStream au lieu d'une chaîne, c'est ce que je dis. :) Et bon d'entendre parler de l'exception - je ne savais pas si cela avait été coupé et collé à partir de votre code réel ou simplement d'un exemple de code pour StackOverflow.
1
Aussi, concernant la construction d'un produit qui communiquera avec beaucoup de systèmes externes, j'hésiterais à construire n'importe quel code générique sans exigences concrètes. Par exemple, jusqu'à ce que vous ayez au moins deux types d'objet à analyser, ou deux formats de fichier, dont vous avez besoin, je ne ferais pas une interface générique d'analyseur.
Je vais réfléchir à ce que vous dites. Je voudrais souligner qu'il existe 4 fichiers xml différents contenant 4 types de données différents à analyser. Les données produit ne sont qu'un type de données à consommer par notre système / produit.
CKing
J'ai encore une question pour vous. Je ne vais pas utiliser un contexte qui fait partie du modèle de stratégie. Est-ce que ça ira? Je me débarrasse également des paramètres génériques et retourne Object dans la méthode d'analyse dans l'interface Parser. Cela permet d'éviter que les classes qui utilisent l'analyseur soient déclarées avec un paramètre de type.
CKing le
1

Votre conception n'est pas une meilleure option. Par votre conception, la seule façon de l'utiliser:

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

Nous ne pouvons pas voir trop d'avantages de l'exemple ci-dessus. Nous ne pouvons pas faire des choses comme ça:

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

Vous pouvez envisager les deux options suivantes avant de rechercher le générique:

  • 1, même sortie après analyse

Peu importe d'où vient la source de données, les données du produit seront au même format avant de les enregistrer dans la base de données. C'est le contrat entre le client et votre service de vidage. Je suppose donc que vous avez les mêmes ProductData en sortie. Vous pouvez simplement définir une interface:

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

De plus, vous définissez ProductData comme interface si vous le souhaitez plus flexible.

Si vous ne voulez pas que l'analyseur soit mélangé avec les données. Vous pouvez le diviser en deux interfaces:

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

Et votre analyseur ressemblera à ceci:

public class XMLParser implements Parser, Data {} 
public class TextParser implements Parser, Data {}
  • 2, sortie différente après l'analyse

Si le ProductData n'est pas similaire et que vous souhaitez réutiliser l'interface Parser. Vous pouvez le faire de cette façon:

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();        
}
Canhua Li
la source
-2

Juste au cas où vous préféreriez utiliser quelque chose déjà disponible, j'ai créé une bibliothèque java appelée JRecordBind qui est basée sur XMLSchema (soutenu par JAXB).

Il est né pour consommer / produire des fichiers de longueur fixe et, puisque XMLSchema définit leur structure, vous pouvez l'utiliser avec du JAXB simple pour rassembler / dé-marshaller des fichiers XML

Federico Fissore
la source
Je recherche un design pour implémenter un analyseur générique! Je ne pense pas que vous ayez bien compris ma question. :)
CKing