Thiết kế trình phân tích tệp chung trong Java bằng cách sử dụng mẫu Chiến lược


14

Tôi đang làm việc trên một sản phẩm trong đó trách nhiệm của một trong các mô-đun là phân tích các tệp XML và kết xuất nội dung cần thiết trong cơ sở dữ liệu. Mặc dù yêu cầu hiện tại chỉ là phân tích các tệp XML, tôi muốn thiết kế mô-đun phân tích cú pháp của mình theo cách mà tôi có thể hỗ trợ bất kỳ loại tệp nào trong tương lai. Lý do cho cách tiếp cận này là chúng tôi đang xây dựng sản phẩm này cho một khách hàng cụ thể nhưng dự định bán nó cho các khách hàng khác trong tương lai gần. Tất cả các hệ thống trong hệ sinh thái cho máy khách hiện tại tạo và tiêu thụ các tệp XML nhưng điều này có thể không xảy ra đối với các máy khách khác.

Tôi đã thử những gì cho đến nay? (Hiện tại) Tôi có ý tưởng thiết kế sau dựa trên mẫu Chiến lược. Tôi đã nhanh chóng viết mã trong nhật thực để truyền đạt thiết kế của mình để thật tuyệt nếu các khía cạnh khác như cách xử lý ngoại lệ thích hợp bị bỏ qua cho đến bây giờ.

Parser: Giao diện chiến lược trưng ra một phương thức phân tích cú pháp.

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

* Lý do sử dụng một tham số chung là để cho phép bất kỳ loại trả về cũng như đảm bảo an toàn loại tại thời gian biên dịch.

ProductDataXmlParser Một lớp cụ thể để phân tích tệp sản phẩm có chứa thông tin liên quan đến sản phẩm. (sử dụng 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();
    }
} 

trong đó : ProductDataTYPE và ProductDataDocument là các lớp POJO của XMlBean được tạo bằng xsd và lệnh scomp.

Tương lai

Nếu tôi có một tệp sản phẩm được phân tích cú pháp trong tương lai, tôi có thể xác định POJO của riêng mình được gọi là ProductData sẽ chứa các nội dung bắt buộc của tệp. Sau đó, tôi có thể tạo một lớp cụ thể có tên là ProductDataFlatFileParser thực hiện giao diện Parser và có phương thức phân tích cú pháp cho ProductData POJO cho tôi sau khi phân tích tệp.

Liệu thiết kế này có ý nghĩa? Có bất kỳ sai sót rõ ràng trong thiết kế này? Khi thiết kế đứng, tôi cho phép các lớp cụ thể xác định thuật toán phân tích một tệp và để lớp cụ thể quyết định nơi để điền dữ liệu. Thiết kế dường như phụ thuộc nhiều hơn vào các đối tượng miền hơn là các định dạng tệp. đây là một điều xấu? Bất kỳ đầu vào về cách tôi có thể cải thiện thiết kế của tôi sẽ được đánh giá cao.


Phần mềm không nên cho người gọi biết định dạng tệp nào được hỗ trợ? Làm thế nào để phần mềm của bạn biết trình phân tích cú pháp nào để gọi?
tomdemuyt

Bạn đang tìm kiếm phản hồi về thiết kế của bạn chứ không phải triển khai thực tế của bạn , vì vậy điều này sẽ được chuyển sang Lập trình viên, nơi nó thuộc chủ đề.
mã công viên

@tomdemuyt Hãy nghĩ mô hình nhà máy;)
CKing

2
@bot Người dùng SO đã bảo bạn đăng bài này lên Code Review rõ ràng là sai. Bạn có thể đã đọc Câu hỏi thường gặp của trang web trước khi đăng nó, "ai đó bảo tôi làm điều đó" thực sự không phải là lý do chính đáng để bạn làm bất cứ điều gì. Không ai chơi bóng bàn với nó, ai đó đã tình nguyện dành thời gian của họ và cố gắng tìm ra một nơi tốt hơn cho nó thay vì đóng hoàn toàn nó (đó sẽ là một lựa chọn hợp lệ, vì nó không phải là chủ đề cho Đánh giá mã).
yannis

2
Xin đừng vượt qua, hoặc. Bạn đang làm cho một mớ hỗn độn, chúng ta phải dọn dẹp.
Tách ra

Câu trả lời:


7

Tôi có một vài lo ngại:

  1. Tôi chắc chắn rằng bạn thực sự cần một thiết kế chung trước khi thực hiện. Bạn có chắc chắn bạn sẽ cần các loại tệp khác với XML không? Nếu không, tại sao mã cho họ? Nếu cuối cùng bạn cần nó, bạn có thể trang bị thêm mã của mình tại thời điểm đó. Sẽ không mất nhiều thời gian nữa, bạn có thể sẽ có các yêu cầu khác sẽ làm cho mã trông khác với những gì bạn đang đề xuất và có lẽ bạn sẽ không bao giờ cần phải viết nó. Như họ nói, YAGNI (Bạn không cần nó).
  2. Nếu bạn thực sự cần một thiết kế chung chung và bạn khá chắc chắn về điều này, thì tôi sẽ nói rằng Parser<T>về cơ bản đó là âm thanh. Tôi thấy có hai vấn đề tiềm ẩn: (1) nó giả sử đầu vào tệp - nếu bạn đang cố phân tích luồng JSON mà bạn đã truy xuất từ ​​phản hồi HTTP chẳng hạn thì sao? và (2) nó không nhất thiết phải cung cấp nhiều giá trị ngoại trừ một phần của một khung chung chung lớn hơn, nơi bạn có rất nhiều loại trình phân tích cú pháp khác nhau cho nhiều loại dữ liệu khác nhau. Nhưng tôi không tin bạn cần bất kỳ khuôn khổ chung lớn như vậy. Bạn chỉ cần có một trường hợp sử dụng cụ thể, đơn giản ngay bây giờ, theo như tôi có thể nói: phân tích một tệp XML thành một danh sách ProductDatas.
  3. Gần như không bao giờ là một ý tưởng tốt để nuốt các ngoại lệ như bạn đang làm ProductDataXmlParser. Tôi sẽ chuyển đổi nó để một số loại RuntimeExceptionthay thế.

1
Chúng tôi đang xây dựng một sản phẩm sẽ giao tiếp với rất nhiều hệ thống bên ngoài vì vậy tôi đoán rằng đó sẽ là một ý tưởng tốt để tính đến bất kỳ loại định dạng tệp / đầu vào nào. Điểm tuyệt vời về Luồng JSON. Đó chính xác là lý do tại sao tôi có phương thức phân tích cú pháp trong giao diện Parser lấy tham số String thay vì tham số File. Tôi đã có một lỗi nhỏ trong ProductDataXmlParser mà tôi đã sửa (Cần truyền tệp cho trình phân tích cú pháp XmlBean). Bạn cũng đúng về việc nuốt ngoại lệ. Tôi đã viết mã này một cách nhanh chóng trong nhật thực để chuyển tải thiết kế của mình trên stackoverflow thông qua một ví dụ;)
CKing

OK, tuyệt. Tôi đoán tôi sẽ biến tham số Parser thành InputStream thay vì String, đó là những gì tôi đang nói. :) Và thật tốt khi nghe về ngoại lệ - Tôi không chắc liệu nó đã bị cắt từ mã thực tế của bạn hay chỉ là mã mẫu cho StackOverflow.

1
Ngoài ra, liên quan đến việc xây dựng một sản phẩm sẽ giao tiếp với nhiều hệ thống bên ngoài, tôi sẽ ngần ngại xây dựng bất kỳ mã chung nào mà không có yêu cầu cụ thể. Ví dụ: cho đến khi bạn có ít nhất hai loại đối tượng để phân tích hoặc hai định dạng tệp mà bạn cần, tôi sẽ không tạo giao diện Parser chung.

Tôi sẽ suy nghĩ về những gì bạn đang nói. Tôi muốn chỉ ra rằng có 4 tệp xml khác nhau chứa 4 loại dữ liệu khác nhau sẽ được phân tích cú pháp. Dữ liệu sản phẩm chỉ là một loại dữ liệu được sử dụng bởi hệ thống / sản phẩm của chúng tôi.
CKing

Tôi có một câu hỏi nữa cho bạn. Tôi sẽ không sử dụng Bối cảnh là một phần của mẫu Chiến lược. Điều đó sẽ ổn chứ? Tôi cũng đang loại bỏ các tham số chung và trả về Object trong phương thức phân tích cú pháp trong giao diện Parser. Điều này là để tránh các lớp sử dụng Trình phân tích cú pháp được khai báo với tham số kiểu.
CKing

1

Thiết kế của bạn không phải là một lựa chọn tốt nhất. Theo thiết kế của bạn, cách duy nhất để sử dụng nó:

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

Chúng ta không thể thấy quá nhiều lợi ích từ ví dụ trên. Chúng ta không thể làm những việc như thế này:

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

Bạn có thể xem xét hai tùy chọn sau trước khi tìm kiếm chung:

  • 1, Cùng một đầu ra sau khi phân tích cú pháp

Bất kể nguồn dữ liệu đến từ đâu, dữ liệu sản phẩm sẽ có cùng định dạng trước khi bạn lưu nó vào cơ sở dữ liệu. Đó là hợp đồng giữa khách hàng và dịch vụ kết xuất của bạn. Vì vậy, tôi giả sử bạn có ProductData giống như đầu ra. Bạn chỉ có thể xác định một giao diện:

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

Ngoài ra, bạn xác định ProductData là giao diện nếu bạn muốn nó linh hoạt hơn.

Nếu bạn không muốn Parser trộn lẫn với dữ liệu. Bạn có thể chia nó thành hai giao diện:

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

Và trình phân tích cú pháp của bạn sẽ trông như thế này:

public class XMLParser implements Parser, Data {} 
public class TextParser implements Parser, Data {}
  • 2, đầu ra khác nhau sau khi phân tích cú pháp

Nếu ProductData không giống nhau và bạn muốn sử dụng lại giao diện Parser. Bạn có thể làm theo cách này:

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

Chỉ trong trường hợp bạn muốn sử dụng thứ gì đó đã có sẵn, tôi đã tạo một thư viện java có tên là JRecordBind dựa trên XMLSchema (được hỗ trợ bởi JAXB).

Nó được sinh ra để tiêu thụ / sản xuất các tệp có độ dài cố định và do XMLSchema xác định cấu trúc của chúng, bạn có thể sử dụng nó với JAXB đơn giản để tạo các tệp XML marshall / unmarshall


Tôi đang tìm kiếm một thiết kế để thực hiện một trình phân tích cú pháp chung! Tôi không nghĩ bạn đã trả lời đúng câu hỏi của tôi. :)
CKing
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.