Desain parser file generik di Jawa menggunakan pola Strategi

14

Saya sedang mengerjakan suatu produk di mana tanggung jawab salah satu modul adalah mem-parsing file XML dan membuang konten yang diperlukan dalam database. Meskipun persyaratan saat ini hanya untuk mem-parsing file XML, saya ingin merancang modul parsing saya dengan cara yang saya dapat mendukung semua jenis file di masa depan. Alasan untuk pendekatan ini adalah bahwa kami sedang membangun produk ini untuk klien tertentu tetapi berencana untuk menjualnya ke klien lain dalam waktu dekat. Semua sistem dalam ekosistem untuk klien saat ini menghasilkan dan menggunakan file XML tetapi ini mungkin tidak berlaku untuk klien lain.

Apa yang sudah saya coba sejauh ini? (Hadiah) Saya memiliki desain berikut dalam pikiran yang didasarkan pada pola Strategi. Saya telah dengan cepat menuliskan kode dalam gerhana untuk menyampaikan desain saya sehingga akan lebih bagus jika aspek-aspek lain seperti cara penanganan pengecualian yang tepat diabaikan untuk saat ini.

Parser: Antarmuka strategi yang memperlihatkan metode parse.

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

* Alasan untuk menggunakan parameter generik adalah untuk memungkinkan semua jenis pengembalian serta memastikan keamanan jenis pada waktu kompilasi.

ProductDataXmlParser Kelas konkret untuk mem-parsing file product.xml yang berisi informasi terkait produk. (menggunakan 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();
    }
} 

di mana : ProductDataTYPE dan ProductDataDocument adalah kelas XMlBean POJO yang dihasilkan menggunakan xsd dan perintah scomp.

Masa depan

Jika saya memiliki file product.txt untuk diuraikan di masa mendatang, saya dapat menentukan POJO saya sendiri yang disebut ProductData yang akan menampung konten yang diperlukan dari file tersebut. Saya kemudian dapat membuat kelas konkret yang disebut ProductDataFlatFileParser yang mengimplementasikan antarmuka Parser dan meminta metode parse mengisi POJO ProductData untuk saya setelah mem-parsing file.

Apakah desain ini masuk akal? Apakah ada kekurangan yang jelas dalam desain ini? Sebagai desain berdiri, saya membiarkan kelas beton untuk mendefinisikan algoritma untuk mem-parsing file dan membiarkan kelas beton memutuskan di mana mengisi data. Desain tampaknya lebih bergantung pada objek domain daripada format file. Apakah ini hal yang buruk? Setiap masukan tentang bagaimana saya dapat meningkatkan desain saya akan sangat dihargai.

CKing
sumber
Haruskah perangkat lunak tidak membiarkan pemanggil tahu format file apa yang didukung? Bagaimana perangkat lunak Anda tahu parser mana yang harus dipanggil?
tomdemuyt
Anda mencari umpan balik tentang desain Anda , bukan implementasi Anda yang sebenarnya , jadi ini akan dimigrasi ke Programmer di mana itu pada topik.
codesparkle
@tomdemuyt Pikirkan pola pabrik;)
CKing
2
@bot Pengguna SO yang menyuruh Anda untuk memposting ini di Code Review jelas salah. Anda bisa membaca FAQ situs sebelum mempostingnya, "seseorang menyuruh saya melakukannya" sebenarnya bukan alasan yang baik bagi Anda untuk melakukan apa pun. Tidak ada yang bermain ping pong dengannya, seseorang menawarkan waktu mereka dan mencoba mencari tempat yang lebih baik daripada langsung menutupnya (yang akan menjadi pilihan yang valid, karena itu di luar topik untuk Code Review).
yannis
2
Tolong jangan lintas pos juga. Anda membuat kekacauan kita harus membersihkan.
Ripped Off

Jawaban:

7

Saya memiliki beberapa masalah:

  1. Saya akan memastikan Anda benar-benar membutuhkan desain generik sebelum menerapkannya. Apakah Anda yakin akan memerlukan jenis file selain XML? Jika tidak, mengapa kode untuk mereka? Jika pada akhirnya Anda membutuhkannya, Anda dapat memperbaiki kode Anda pada saat itu. Tidak akan memakan waktu lebih lama, Anda mungkin akan memiliki persyaratan lain yang akan membuat kode terlihat berbeda dari yang Anda usulkan saat ini, dan Anda mungkin tidak akan pernah perlu menulisnya lagi. Seperti yang mereka katakan, YAGNI (Kamu Tidak Akan Membutuhkannya).
  2. Jika Anda benar-benar membutuhkan desain generik, dan Anda cukup yakin dengan ini, maka saya akan mengatakan bahwa Parser<T>pada dasarnya suara. Saya memang melihat dua masalah potensial: (1) mengasumsikan input file - bagaimana jika Anda mencoba mengurai aliran JSON yang Anda ambil dari respons HTTP, misalnya? dan (2) itu tidak selalu memberikan banyak nilai kecuali sebagai bagian dari beberapa kerangka kerja generik yang lebih besar di mana Anda memiliki banyak jenis parser yang berbeda untuk banyak jenis data yang berbeda. Tapi saya tidak yakin Anda membutuhkan kerangka kerja generik yang begitu besar. Anda hanya memiliki case use yang sangat sederhana dan konkret sekarang, sejauh yang saya tahu: parsing file XML ke dalam daftar ProductDatas.
  3. Hampir tidak pernah merupakan ide bagus untuk menelan pengecualian seperti yang Anda lakukan ProductDataXmlParser. Saya akan mengubahnya menjadi semacam RuntimeExceptiongantinya.

sumber
1
Kami sedang membangun produk yang akan berkomunikasi dengan banyak sistem eksternal, jadi saya kira itu akan menjadi ide yang baik untuk menjelaskan segala jenis format file / input. Poin bagus tentang JSON Stream. Itulah sebabnya saya menggunakan metode parse di antarmuka Parser untuk mengambil parameter String alih-alih parameter File. Saya memiliki kesalahan kecil di ProductDataXmlParser saya yang telah saya koreksi (Perlu untuk mengirimkan file ke parser XmlBean). Anda juga benar tentang menelan pengecualian. Saya menuliskan kode ini dengan cepat di gerhana untuk menyampaikan desain saya di stackoverflow melalui contoh;)
CKing
OK keren. Saya kira saya akan membuat parameter Parser menjadi InputStream alih-alih sebuah String, itulah yang saya katakan. :) Dan senang mendengar tentang pengecualian - Saya tidak yakin apakah itu dipotong 'n' disisipkan dari kode Anda yang sebenarnya atau hanya kode sampel untuk StackOverflow.
1
Juga, mengenai membangun produk yang akan berkomunikasi dengan banyak sistem eksternal, saya akan ragu untuk membangun kode generik apa pun tanpa persyaratan konkret. Misalnya, sampai Anda memiliki setidaknya dua jenis objek untuk diuraikan, atau dua format file, yang Anda butuhkan, saya tidak akan membuat antarmuka Parser generik.
Saya akan memikirkan apa yang Anda katakan. Saya ingin menunjukkan bahwa ada 4 file xml berbeda yang berisi 4 tipe data berbeda untuk diuraikan. Data produk hanyalah satu jenis data yang akan dikonsumsi oleh sistem / produk kami.
CKing
Saya punya satu pertanyaan lagi untuk Anda. Saya tidak akan menggunakan Konteks yang merupakan bagian dari pola Strategi. Apakah itu akan baik-baik saja? Saya juga menyingkirkan parameter umum dan mengembalikan Object dalam metode parse di antarmuka Parser. Ini untuk menghindari kelas yang menggunakan Parser untuk dideklarasikan dengan parameter tipe.
CKing
1

Desain Anda bukan pilihan terbaik. Dengan desain Anda, satu-satunya cara untuk menggunakannya:

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

Kita tidak dapat melihat terlalu banyak manfaat dari contoh di atas. Kami tidak dapat melakukan hal-hal seperti ini:

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

Anda dapat mempertimbangkan dua opsi berikut sebelum mencari yang umum:

  • 1, output yang sama setelah parse

Tidak masalah dari mana sumber data berasal, data produk akan memiliki format yang sama sebelum Anda menyimpannya ke basis data. Ini adalah kontrak antara klien dan layanan pembuangan Anda. Jadi saya menganggap Anda memiliki ProductData yang sama dengan output. Anda cukup mendefinisikan antarmuka:

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

Selain itu Anda mendefinisikan ProductData sebagai antarmuka jika Anda ingin lebih fleksibel.

Jika Anda tidak ingin Parser dicampur dengan data. Anda dapat membaginya menjadi dua antarmuka:

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

Dan parser Anda akan terlihat seperti ini:

public class XMLParser implements Parser, Data {} 
public class TextParser implements Parser, Data {}
  • 2, keluaran berbeda setelah parse

Jika ProductData tidak serupa dan Anda ingin menggunakan kembali antarmuka Parser. Anda bisa melakukannya dengan cara ini:

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

Kalau-kalau Anda lebih suka menggunakan sesuatu yang sudah tersedia, saya telah membuat perpustakaan java bernama JRecordBind yang didasarkan pada XMLSchema (didukung oleh JAXB).

Itu lahir untuk mengkonsumsi / menghasilkan file dengan panjang tetap, dan, karena XMLSchema mendefinisikan struktur mereka, Anda dapat menggunakannya dengan JAXB biasa untuk marshall / unmarshall file XML

Federico Fissore
sumber
Saya mencari desain untuk mengimplementasikan parser generik! Saya tidak berpikir Anda mengerti pertanyaan saya dengan benar. :)
CKing