전략 패턴을 사용하는 Java의 일반 파일 파서 디자인


14

모듈 중 하나의 책임이 XML 파일을 구문 분석하고 필요한 내용을 데이터베이스에 덤프하는 제품을 개발 중입니다. 현재 요구 사항은 XML 파일 만 구문 분석하는 것이지만 향후 모든 종류의 파일을 지원할 수있는 방식으로 구문 분석 모듈을 설계하려고합니다. 이 접근 방식의 이유는 특정 고객을 위해이 제품을 구축하고 있지만 가까운 시일 내에 다른 고객에게 판매 할 계획이기 때문입니다. 현재 클라이언트에 대한 에코 시스템의 모든 시스템은 XML 파일을 생성하고 소비하지만 다른 클라이언트에게는 해당되지 않을 수 있습니다.

지금까지 무엇을 시도 했습니까? (현재) 전략 패턴을 기반으로하는 다음과 같은 디자인을 염두에두고 있습니다. 내 디자인을 전달하기 위해 이클립스로 코드를 신속하게 작성 했으므로 예외 처리의 적절한 방법과 같은 다른 측면이 무시되는 것이 좋습니다.

파서 : 파싱 ​​방법을 공개하는 전략 인터페이스.

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

* 일반 매개 변수를 사용하는 이유는 모든 리턴 유형을 허용하고 컴파일시 유형 안전을 보장하기위한 것입니다.

ProductDataXmlParser 제품 관련 정보가 포함 된 product.xml 파일을 구문 분석하기위한 구체적 클래스입니다. (XMLBean 사용)

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 및 ProductDataDocument는 xsd 및 scomp 명령을 사용하여 생성 된 XMlBean POJO 클래스입니다.

미래

나중에 구문 분석 할 product.txt 파일이있는 경우 파일의 필수 컨텐츠를 보유 할 ProductData라는 자체 POJO를 정의 할 수 있습니다. 그런 다음 파서 인터페이스를 구현하고 파일 구문 분석 후 구문 분석 메소드가 ProductData POJO를 채우도록 ProductDataFlatFileParser라는 구체적인 클래스를 작성할 수 있습니다.

이 디자인이 의미가 있습니까? 이 디자인에 명백한 결함이 있습니까? 디자인이 의미하는대로, 구체적인 클래스가 알고리즘을 정의하여 파일을 구문 분석하고 콘크리트 클래스가 데이터를 채울 위치를 결정할 수있게합니다. 디자인은 파일 형식이 아닌 도메인 개체에 더 의존적 인 것 같습니다. 이것은 나쁜 것입니까? 디자인을 개선 할 수있는 방법에 대한 의견을 보내 주시면 감사하겠습니다.


소프트웨어가 발신자에게 어떤 파일 형식이 지원되는지 알려주지 않아야합니까? 소프트웨어는 어떤 파서를 호출해야하는지 어떻게 알 수 있습니까?
— tomdemuyt

실제 구현 이 아닌 설계 에 대한 피드백을 찾고 있으므로 주제에 관한 프로그래머에게 마이그레이션됩니다.
— codesparkle

@tomdemuyt 공장 패턴을 생각 ;)
— CKing

2
@bot Code Review에 이것을 게시하라는 SO 사용자는 분명히 잘못되었습니다. 게시하기 전에 사이트의 FAQ를 읽었을 수 있습니다. "누군가가 요청한 것"은 실제로 수행해야 할 좋은 이유가 아닙니다. 아무도 탁구를 가지고 노는 사람이 없으며 누군가 가 시간을내어 자원 봉사 하고 그것을 완전히 닫는 대신에 더 좋은 곳을 찾으려고 노력했습니다.
— yannis

2
크로스 포스트도하지 마십시오. 당신은 우리가 청소해야 엉망입니다.
— 17:01에

답변:


7

몇 가지 우려 사항이 있습니다.

  1. 실제로 구현하기 전에 일반적인 디자인이 필요한지 확인합니다. XML 이외의 파일 형식이 필요합니까? 그렇지 않다면 왜 코드를 작성해야합니까? 결국 필요할 경우 해당 시점에서 코드를 개조 할 수 있습니다. 시간이 오래 걸리지 않을 것입니다. 현재 제안하는 것과 코드가 다르게 보이는 다른 요구 사항이있을 수 있으며 어쨌든 코드를 작성할 필요가 없습니다. 그들이 말했듯이 YAGNI (당신은 그것을 필요로하지 않습니다).
  2. 실제로 일반적인 디자인이 필요하고 이것을 확신한다면 Parser<T>기본적으로 건전 하다고 말할 것 입니다. 두 가지 잠재적 인 문제가 있습니다. (1) 파일 입력을 가정합니다. 예를 들어 HTTP 응답에서 검색 한 JSON 스트림을 구문 분석하려고하면 어떻게됩니까? (2) 다양한 유형의 데이터에 대해 다양한 유형의 파서가있는 더 큰 일반 프레임 워크의 일부를 제외하고는 반드시 많은 가치를 제공하지는 않습니다. 그러나 나는 당신이 그렇게 큰 일반적인 프레임 워크가 필요하다고 확신하지 않습니다. 내가 알 수있는 한 지금 매우 간단하고 구체적인 유스 케이스가 있습니다 .XML 파일을의 목록으로 구문 분석하십시오 ProductData.
  3. 에서하는 것처럼 예외를 삼키는 것은 좋은 생각이 아닙니다 ProductDataXmlParser. RuntimeException대신 일종의 것으로 변환합니다 .

1
우리는 많은 외부 시스템과 통신 할 수있는 제품을 만들고 있으므로 모든 종류의 파일 / 입력 형식을 고려하는 것이 좋습니다. JSON 스트림에 대한 훌륭한 지적. 이것이 바로 파서 인터페이스의 구문 분석 메소드가 File 매개 변수 대신 String 매개 변수를 취하는 이유입니다. ProductDataXmlParser에서 수정 한 작은 실수가있었습니다 (파일을 XmlBean 파서에 전달해야 함). 예외를 삼키는 것에 대해서도 옳습니다. 나는 예제를 통해 stackoverflow에 대한 디자인을 전달하기 위해 일식으로이 코드를 빠르게 작성했다.)
— CKing

그래 좋아. Parser 매개 변수를 String 대신 InputStream으로 만들 것이라고 생각합니다. :) 그리고 예외에 대해 들었습니다. 실제 코드 또는 StackOverflow에 대한 샘플 코드에서 붙여 넣은 것인지 확실하지 않았습니다.

1
또한 많은 외부 시스템과 통신 할 수있는 제품을 구축 할 때 구체적인 요구 사항없이 일반 코드를 작성하는 것을 망설입니다. 예를 들어 구문 분석 할 객체 유형이 두 개 이상 필요하거나 파일 형식이 두 개 이상 필요할 때까지 일반 파서 인터페이스를 만들지 않습니다.

나는 당신이 말하는 것에 대해 생각할 것입니다. 파싱 ​​할 4 가지 유형의 데이터가 포함 된 4 가지 XML 파일이 있음을 지적하고 싶습니다. 제품 데이터는 시스템 / 제품이 소비하는 한 가지 유형의 데이터입니다.
— CKing 2019

질문이 하나 더 있습니다. 전략 패턴의 일부인 컨텍스트를 사용하지 않겠습니다. 괜찮을까요? 또한 일반 매개 변수를 제거하고 Parser 인터페이스의 구문 분석 메소드에서 Object를 반환합니다. 이것은 파서를 사용하는 클래스가 형식 매개 변수로 선언되는 것을 피하기 위해입니다.
— CKing

1

디자인이 최선의 선택은 아닙니다. 디자인에 따라 사용하는 유일한 방법은 다음과 같습니다.

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

위의 예제에서 많은 이점을 볼 수는 없습니다. 우리는 다음과 같은 일을 할 수 없습니다 :

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

일반을 찾기 전에 다음 두 가지 옵션을 고려할 수 있습니다.

  • 1, 구문 분석 후 동일한 출력

데이터 소스의 출처에 관계없이 제품 데이터는 데이터베이스에 저장하기 전에 동일한 형식입니다. 클라이언트와 덤프 서비스 간의 계약입니다. 따라서 출력과 동일한 ProductData가 있다고 가정합니다. 인터페이스를 간단하게 정의 할 수 있습니다.

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

또한보다 유연하게 제품 데이터를 인터페이스로 정의합니다.

파서가 데이터와 혼합되는 것을 원하지 않는 경우. 두 개의 인터페이스로 분할 할 수 있습니다.

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가 유사하지 않고 파서 인터페이스를 재사용하려는 경우. 이 방법으로 할 수 있습니다 :

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();        
}

-2

이미 사용 가능한 것을 사용하려는 경우를 대비하여 JRecordBind 라는 Java 라이브러리를 만들었습니다. 하여 XMLSchema (JAXB 지원)를 기반으로하는 .

고정 길이 파일을 소비 / 생성하기 위해 탄생했으며 XMLSchema가 구조를 정의하므로 일반 JAXB와 함께 사용하여 XML 파일을 마샬링 / 비 정렬화할 수 있습니다.


일반 파서를 구현하는 디자인을 찾고 있습니다! 나는 당신이 내 질문을 올바르게 이해하지 못했다고 생각합니다. :)
— CKing
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.