解釋器模式
定義
解釋器模式(Interpreter Pattern
):定義一個語言的文法,並且建立一個解釋器來解釋該語言中的句子,這裏的“語言”是指使用規定格式和語法的代碼。解釋器模式是一種類行爲型模式。
場景
光看定義或名字就能明白,解釋其模式就是將一定的文法解釋成程序可以理解的結構,然後輸出結果,比如輸入一個四則運算表達式計算出結果、寫一個計算機語言的解釋器等等。
現在有一個程序要讀取oracle的配置文件,比如tnsname.ora,listener.ora等以ora結尾的配置文件,返回可以在代碼中可以操作的數據結構。首先是不要重複造輪子,幾經搜索發現並沒有這樣的工具,於是查閱官方文檔,根據語法規則自己實現一個解釋器,語法規則參考:Syntax Rules for Configuration Files
UML類圖
代碼
interpreter
示例:
public class TestInterpreter {
@Test
public void test() throws OrafileParseException {
OraFileParserContext context = new OraFileParserContext("# tnsnames.ora Network Configuration File: /u01/app/oracle/product/12.2.0.1/dbhome_1/network/admin/tnsnames.ora\n" +
"# Generated by Oracle configuration tools.\n" +
"\n" +
"\n" +
"LISTENER_ST12201 =\n" +
" (ADDRESS_LIST =\n" +
" (ADDRESS = (PROTOCOL = TCP)(HOST = standalone)(PORT = 1521))\n" +
" (ADDRESS = (PROTOCOL = IPC)(HOST = standalone)(KEY = LISTENER_DG))\n" +
" )\n" +
"\n" +
"\n" +
"\n" +
"\n" +
"ST12201DG2 =\n" +
" (DESCRIPTION =\n" +
" (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.20.26 )(PORT = 1522))\n" +
" (CONNECT_DATA =\n" +
" (SERVER = DEDICATED)\n" +
" (SERVICE_NAME = ST12201DG2)\n" +
" )\n" +
" )\n" +
"\n" +
"ST12201 =\n" +
" (DESCRIPTION =\n" +
" (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.20.114 )(PORT = 1522))\n" +
" (CONNECT_DATA =\n" +
" (SERVER = DEDICATED)\n" +
" (SERVICE_NAME = ST12201)\n" +
" )\n" +
" )\n" +
"\n" +
"ST12201DG =\n" +
" (DESCRIPTION =\n" +
" (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.20.19 )(PORT = 1522))\n" +
" (CONNECT_DATA =\n" +
" (SERVER = DEDICATED)\n" +
" (SERVICE_NAME = ST12201DG)\n" +
" )\n" +
" )");
OraFile oraFile = new OraFile();
oraFile.interpreter(context);
System.out.println(oraFile.toString());
System.out.println(oraFile.toOraFile());
}
@Test
public void test1() throws OrafileParseException {
OraFileParserContext context = new OraFileParserContext(
"LISTENER_ST12201=" +
" (ADDRESS_LIST=" +
" (ADDRESS = ((PROTOCOL = TCP)(HOST = standalone)(PORT = 1521)))" +
" )"
);
OraFile oraFile = new OraFile();
oraFile.interpreter(context);
System.out.println(oraFile.toString());
}
@Test
public void test2() throws OrafileParseException {
OraFileParserContext context = new OraFileParserContext("LISTENER_ST12201 =\n" +
" (ADDRESS_LIST =\n" +
" (ADDRESS = (PROTOCOL = TCP)(HOST = standalone)(PORT = 1521))\n" +
" (ADDRESS = (PROTOCOL = IPC)(HOST = standalone)(KEY = LISTENER_DG))\n" +
" )");
OraFile oraFile = new OraFile();
oraFile.interpreter(context);
System.out.println(oraFile.toString());
}
@Test
public void test3() throws OrafileParseException {
OraFileParserContext context = new OraFileParserContext("a=b\nb=(c=d)(e=f)");
OraFile oraFile = new OraFile();
oraFile.interpreter(context);
System.out.println(oraFile.toString());
}
@Test
public void test4() throws OrafileParseException {
OraFileParserContext context = new OraFileParserContext("a=b\nb=(c='d\\'\\#\\(\\)\\=')(e=f)");
OraFile oraFile = new OraFile();
oraFile.interpreter(context);
System.out.println(oraFile.toString());
}
@Test
public void test5() throws OrafileParseException {
OraFileParserContext context = new OraFileParserContext("a=b\nb=(c='vvv')(e=f)");
OraFile oraFile = new OraFile();
oraFile.interpreter(context);
System.out.println(oraFile.toString());
}
}
}
總結
解釋器模式的主要優點如下:
- 易於改變和擴展文法。由於在解釋器模式中使用類來表示語言的文法規則,因此可以通過繼承等機制來改變或擴展文法。
- 每一條文法規則都可以表示爲一個類,因此可以方便地實現一個簡單的語言。
- 實現文法較爲容易。在抽象語法樹中每一個表達式節點類的實現方式都是相似的,這些類的代碼編寫都不會特別複雜,還可以通過一些工具自動生成節點類代碼。
- 增加新的解釋表達式較爲方便。如果用戶需要增加新的解釋表達式只需要對應增加一個新的終結符表達式或非終結符表達式類,原有表達式類代碼無須修改,符合“開閉原則”。
解釋器模式的主要缺點如下:
- 對於複雜文法難以維護。在解釋器模式中,每一條規則至少需要定義一個類,因此如果一個語言包含太多文法規則,類的個數將會急劇增加,導致系統難以管理和維護,此時可以考慮使用語法分析程序等方式來取代解釋器模式。
- 執行效率較低。由於在解釋器模式中使用了大量的循環和遞歸調用,因此在解釋較爲複雜的句子時其速度很慢,而且代碼的調試過程也比較麻煩。
在以下情況下可以考慮使用解釋器模式:
- 可以將一個需要解釋執行的語言中的句子表示爲一個抽象語法樹。
- 一些重複出現的問題可以用一種簡單的語言來進行表達。
- 一個語言的文法較爲簡單。
- 執行效率不是關鍵問題。【注:高效的解釋器通常不是通過直接解釋抽象語法樹來實現的,而是需要將它們轉換成其他形式,使用解釋器模式的執行效率並不高。】