전략 패턴을 사용하는 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.