Diseño genérico del analizador de archivos en Java utilizando el patrón de estrategia

14

Estoy trabajando en un producto en el que la responsabilidad de uno de los módulos es analizar archivos XML y volcar el contenido requerido en una base de datos. Aunque el requisito actual es solo analizar archivos XML, quiero diseñar mi módulo de análisis de manera que pueda admitir cualquier tipo de archivos en el futuro. La razón de este enfoque es que estamos construyendo este producto para un cliente específico, pero planeamos venderlo a otros clientes en el futuro cercano. Todos los sistemas en el ecosistema para el cliente actual producen y consumen archivos XML, pero este puede no ser el caso para otros clientes.

¿Qué he probado hasta ahora? (El presente) Tengo en mente el siguiente diseño que se basa en el patrón de Estrategia. Rápidamente he escrito el código en eclipse para transmitir mi diseño, por lo que sería genial si otros aspectos, como la forma adecuada de manejar las excepciones, se ignoren por ahora.

Analizador: la interfaz de estrategia que expone un método de análisis.

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

* La razón para usar un parámetro genérico es permitir cualquier tipo de retorno, así como garantizar la seguridad del tipo en tiempo de compilación.

ProductDataXmlParser Una clase concreta para analizar un archivo product.xml que contiene información relacionada con el producto. (usando 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();
    }
} 

donde : ProductDataTYPE y ProductDataDocument son clases POJO XMlBean generadas usando un xsd y el comando scomp.

El futuro

Si tengo un archivo product.txt para analizar en el futuro, puedo definir mi propio POJO llamado ProductData que contendrá el contenido requerido del archivo. Luego puedo crear una clase concreta llamada ProductDataFlatFileParser que implementa la interfaz Parser y hacer que el método de análisis llene el POJO ProductData por mí después de analizar el archivo.

¿Tiene sentido este diseño? ¿Hay defectos obvios en este diseño? Tal como está el diseño, estoy permitiendo que las clases concretas definan el algoritmo para analizar un archivo y que la clase concreta decida dónde poblar los datos. El diseño parece depender más de los objetos de dominio que de los formatos de archivo. ¿Esto es malo? Cualquier aportación sobre cómo puedo mejorar mi diseño será muy apreciada.

CKing
fuente
¿El software no debe informarle a la persona que llama qué formatos de archivo son compatibles? ¿Cómo sabe su software qué analizador invocar?
tomdemuyt
Está buscando comentarios sobre su diseño , no sobre su implementación real , por lo que se migrará a los Programadores donde se trata el tema.
codesparkle
@tomdemuyt Piensa en el patrón de fábrica;)
CKing
2
@bot El usuario SO que le dijo que publicara esto en Code Review obviamente estaba equivocado. Podrías haber leído las preguntas frecuentes del sitio antes de publicarlo, "alguien me dijo que lo hiciera" no es realmente una buena razón para que hagas algo. Nadie juega al ping pong con él, alguien ofreció su tiempo como voluntario e intentó encontrar un lugar mejor para él en lugar de cerrarlo por completo (lo que habría sido una opción válida, ya que está fuera del tema para la Revisión de Código).
Yannis
2
Por favor, no cruces, tampoco. Estás haciendo un desastre que tenemos que limpiar.
Estafado

Respuestas:

7

Tengo un par de preocupaciones:

  1. Me aseguraría de que realmente necesites un diseño genérico antes de implementar uno. ¿Estás seguro de que necesitarás otros tipos de archivos que no sean XML? Si no, ¿por qué codificar para ellos? Si finalmente lo necesita, puede actualizar su código en ese punto. No tomará mucho más tiempo, probablemente tendrá otros requisitos que harán que el código se vea diferente de lo que está proponiendo actualmente, y probablemente nunca tendrá que escribirlo de todos modos. Como dicen, YAGNI (No lo vas a necesitar).
  2. Si realmente necesita un diseño genérico, y está bastante seguro de esto, entonces diría que Parser<T>es básicamente correcto. Veo dos problemas potenciales: (1) supone la entrada del archivo: ¿qué sucede si está intentando analizar una secuencia JSON que recuperó de una respuesta HTTP, por ejemplo? y (2) no necesariamente proporciona mucho valor, excepto como parte de un marco genérico más grande en el que tiene muchos tipos diferentes de analizadores para muchos tipos diferentes de datos. Pero no estoy convencido de que necesite un marco genérico tan grande. Solo tiene un caso de uso muy simple y concreto en este momento, por lo que puedo decir: analizar un archivo XML en una lista de ProductDatas.
  3. Casi nunca es una buena idea tragar excepciones como lo estás haciendo ProductDataXmlParser. Lo convertiría en algún tipo de RuntimeExceptionlugar.

fuente
1
Estamos creando un producto que se comunicará con muchos sistemas externos, así que supongo que sería una buena idea tener en cuenta cualquier tipo de formato de archivo / entrada. Excelente punto sobre la corriente JSON. Es exactamente por eso que hice que mi método de análisis en la interfaz del analizador tomara un parámetro de cadena en lugar de un parámetro de archivo. Tuve un pequeño error en mi ProductDataXmlParser que he corregido (Necesito pasar un archivo al analizador XmlBean). También tiene razón al tragar excepciones. Escribí este código rápidamente en eclipse para transmitir mi diseño en stackoverflow a través de un ejemplo;)
CKing
Está bien. Supongo que haría que el parámetro Parser sea InputStream en lugar de String, es lo que estoy diciendo. :) Y es bueno saber acerca de la excepción: no estaba seguro de si eso fue cortado y pegado de su código real o simplemente código de muestra para StackOverflow.
1
Además, con respecto a la construcción de un producto que se comunique con muchos sistemas externos, dudaría en construir cualquier código genérico sin requisitos concretos. Por ejemplo, hasta que tenga al menos dos tipos de objetos para analizar, o dos formatos de archivo, que necesite, no crearía una interfaz genérica Parser.
Pensaré en lo que estás diciendo. Me gustaría señalar que hay 4 archivos xml diferentes que contienen 4 tipos diferentes de datos para analizar. Los datos del producto son solo un tipo de datos para ser consumidos por nuestro sistema / producto.
CKing
Tengo una pregunta más para ti. No voy a usar un contexto que sea parte del patrón de estrategia. ¿Eso estará bien? También me estoy deshaciendo de los parámetros genéricos y devolviendo Object en el método de análisis en la interfaz Parser. Esto es para evitar que las clases que usan el analizador se declaren con un parámetro de tipo.
CKing
1

Su diseño no es la mejor opción. Por su diseño, la única forma de usarlo:

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

No podemos ver muchos beneficios del ejemplo anterior. No podemos hacer cosas como esta:

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

Puede considerar las siguientes dos opciones antes de buscar el genérico:

  • 1, la misma salida después del análisis

No importa de dónde sea la fuente de datos, los datos del producto tendrán el mismo formato antes de guardarlos en la base de datos. Es el contrato entre el cliente y su servicio de volcado. Así que supongo que tiene los mismos ProductData que la salida. Simplemente puede definir una interfaz:

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

Además, define ProductData como interfaz si desea que sea más flexible.

Si no desea que el analizador se mezcle con los datos. Puede dividirlo en dos interfaces:

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

Y su analizador se verá así:

public class XMLParser implements Parser, Data {} 
public class TextParser implements Parser, Data {}
  • 2, salida diferente después del análisis

Si ProductData no es similar y desea reutilizar la interfaz del analizador. Puedes hacerlo de esta manera:

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
fuente
-2

En caso de que prefiera usar algo ya disponible, he creado una biblioteca java llamada JRecordBind que se basa en XMLSchema (respaldado por JAXB).

Nació para consumir / producir archivos de longitud fija y, dado que XMLSchema define su estructura, puede usarlo con JAXB simple para marshall / unmarshall archivos XML

Federico Fissore
fuente
¡Estoy buscando un diseño para implementar un analizador genérico! No creo que hayas entendido bien mi pregunta. :)
CKing