Corak Reka Bentuk Jurubahasa di Jawa

1. Gambaran keseluruhan

Dalam tutorial ini, kami akan memperkenalkan salah satu corak reka bentuk GoF tingkah laku - Jurubahasa.

Pada mulanya, kami akan memberikan gambaran keseluruhan tujuannya dan menjelaskan masalah yang cuba diselesaikannya.

Kemudian, kita akan melihat rajah UML Interpreter dan pelaksanaan contoh praktikal.

2. Corak Reka Bentuk Jurubahasa

Ringkasnya, corak menentukan tatabahasa bahasa tertentu dengan cara berorientasikan objek yang dapat dinilai oleh jurubahasa itu sendiri.

Oleh itu, secara teknikal kita dapat membina ungkapan reguler biasa kita, jurubahasa DSL khusus atau kita dapat menghuraikan mana-mana bahasa manusia, membina pokok sintaks abstrak dan kemudian menjalankan tafsirannya.

Ini hanya beberapa kes penggunaan yang berpotensi, tetapi jika kita berfikir sebentar, kita dapat menjumpai lebih banyak penggunaannya, misalnya di IDE kita, kerana mereka terus menerus menafsirkan kod yang kita tulis dan dengan itu memberikan petunjuk yang tidak ternilai.

Pola pentafsir pada amnya harus digunakan ketika tatabahasa relatif sederhana.

Jika tidak, ia mungkin sukar dijaga.

3. Diagram UML

Gambar rajah di atas menunjukkan dua entiti utama: Konteks dan Ekspresi .

Sekarang, bahasa apa pun perlu dinyatakan dengan cara tertentu, dan kata-kata (ungkapan) akan mempunyai makna berdasarkan konteks yang diberikan.

AbstractExpression mentakrifkan satu kaedah abstrak yang mengambil kontekssebagai parameter. Berkat itu, setiap ungkapan akan mempengaruhi konteks , mengubah keadaannya dan sama ada meneruskan tafsiran atau mengembalikan hasilnya sendiri.

Oleh itu, konteksnya akan menjadi pemegang keadaan pemprosesan global, dan akan digunakan semula selama keseluruhan proses pentafsiran.

Jadi apa perbezaan antara TerminalExpression dan NonTerminalExpression ?

A NonTerminalExpression boleh mempunyai satu atau lebih lain AbstractExpressions berkaitan di dalamnya, oleh itu ia boleh secara rekursif ditafsirkan. Pada akhirnya, proses pentafsiran harus diakhiri dengan TerminalExpression yang akan mengembalikan hasilnya.

Perlu diingat bahawa NonTerminalExpression adalah gabungan.

Akhirnya, peranan klien adalah untuk membuat atau menggunakan pohon sintaks abstrak yang sudah dibuat , yang tidak lebih daripada ayat yang ditentukan dalam bahasa yang dibuat.

4. Pelaksanaan

Untuk menunjukkan corak dalam tindakan, kami akan membina sintaks seperti SQL sederhana dengan cara yang berorientasikan objek, yang kemudian akan ditafsirkan dan mengembalikan hasilnya kepada kami.

Pertama, kita akan menentukan ungkapan Select, From, dan Where , membina pohon sintaks di kelas klien dan menjalankan tafsirannya.

Antara muka Ekspresi akan mempunyai kaedah tafsiran:

List interpret(Context ctx);

Seterusnya, kita menentukan ungkapan pertama, kelas Pilih :

class Select implements Expression { private String column; private From from; // constructor @Override public List interpret(Context ctx) { ctx.setColumn(column); return from.interpret(ctx); } }

Ia mendapat nama lajur yang akan dipilih dan satu lagi ekspresi konkrit jenis Dari sebagai parameter dalam pembina.

Perhatikan bahawa dalam kaedah tafsir yang ditimpa () ditafsirkan menetapkan keadaan konteks dan meneruskan tafsiran lebih jauh ke ungkapan lain bersama dengan konteksnya.

Dengan cara itu, kita melihat bahawa ia adalah NonTerminalExpression.

Ungkapan lain ialah kelas From :

class From implements Expression { private String table; private Where where; // constructors @Override public List interpret(Context ctx) { ctx.setTable(table); if (where == null) { return ctx.search(); } return where.interpret(ctx); } }

Sekarang, dalam SQL mana klausa adalah pilihan, oleh itu kelas ini adalah ungkapan terminal atau bukan terminal.

Sekiranya pengguna memutuskan untuk tidak menggunakan klausa mana, ungkapan Dari itu akan ditamatkan dengan panggilan ctx.search () dan mengembalikan hasilnya. Jika tidak, ia akan ditafsirkan lebih jauh.

Yang mana ungkapan sekali lagi mengubah suai konteks dengan menetapkan penapis yang diperlukan dan berakhir tafsiran dengan panggilan carian:

class Where implements Expression { private Predicate filter; // constructor @Override public List interpret(Context ctx) { ctx.setFilter(filter); return ctx.search(); } }

Sebagai contoh, kelas Konteks menyimpan data yang meniru jadual pangkalan data.

Perhatikan bahawa ia mempunyai tiga bidang utama yang diubah oleh setiap subkelas Ekspresi dan kaedah carian:

class Context { private static Map
    
      tables = new HashMap(); static { List list = new ArrayList(); list.add(new Row("John", "Doe")); list.add(new Row("Jan", "Kowalski")); list.add(new Row("Dominic", "Doom")); tables.put("people", list); } private String table; private String column; private Predicate whereFilter; // ... List search() { List result = tables.entrySet() .stream() .filter(entry -> entry.getKey().equalsIgnoreCase(table)) .flatMap(entry -> Stream.of(entry.getValue())) .flatMap(Collection::stream) .map(Row::toString) .flatMap(columnMapper) .filter(whereFilter) .collect(Collectors.toList()); clear(); return result; } }
    

Setelah carian selesai, konteksnya akan dikosongkan, jadi lajur, jadual, dan penapis ditetapkan ke lalai.

Dengan cara itu setiap tafsiran tidak akan mempengaruhi yang lain.

5. Menguji

Untuk tujuan ujian, mari kita lihat kelas InterpreterDemo :

public class InterpreterDemo { public static void main(String[] args) { Expression query = new Select("name", new From("people")); Context ctx = new Context(); List result = query.interpret(ctx); System.out.println(result); Expression query2 = new Select("*", new From("people")); List result2 = query2.interpret(ctx); System.out.println(result2); Expression query3 = new Select("name", new From("people", new Where(name -> name.toLowerCase().startsWith("d")))); List result3 = query3.interpret(ctx); System.out.println(result3); } }

Pertama, kita membina pohon sintaks dengan ungkapan yang dibuat, memulakan konteks dan kemudian menjalankan tafsiran. Konteksnya digunakan kembali, tetapi seperti yang kami tunjukkan di atas, ini membersihkan diri setelah setiap panggilan carian.

Dengan menjalankan program, outputnya adalah seperti berikut:

[John, Jan, Dominic] [John Doe, Jan Kowalski, Dominic Doom] [Dominic]

6. Kelemahan

Apabila tatabahasa semakin rumit, semakin sukar untuk dikekalkan.

Ini dapat dilihat pada contoh yang dikemukakan. Cukup mudah untuk menambahkan ungkapan lain, seperti Limit , tetapi tidak akan terlalu mudah untuk dijaga jika kita memutuskan untuk terus memperluasnya dengan semua ungkapan lain.

7. Kesimpulannya

Corak reka bentuk pentafsir sangat sesuai untuk penafsiran tatabahasa yang agak sederhana , yang tidak perlu berkembang dan diperluas.

Dalam contoh di atas, kami menunjukkan bahawa mungkin untuk membuat pertanyaan seperti SQL dengan cara berorientasi objek dengan bantuan corak pentafsir.

Akhirnya, anda boleh menemui penggunaan corak ini di JDK, terutamanya di java.util.Pattern , java.text.Format atau java.text.Normalizer .

Seperti biasa, kod lengkap boleh didapati di projek Github.