場景:讀寫xml文件,如果代碼"寫死了":誰是誰的child,萬一文件父子節點改了,又要改代碼。
1. 正常編碼(不使用模式)
public class ReadAppXml {public void read(String filePathName)throws Exception{Document doc = null
;//建立一個解析器工廠DocumentBuilderFactory factory = DocumentBuilderFactory.
newInstance();//獲得一個DocumentBuilder對象,這個對象代表了具體的DOM解析器DocumentBuilder builder=factory.
newDocumentBuilder();//得到一個表示XML文檔的Document對象doc=builder.
parse(filePathName
);//去掉XML文檔中作為格式化內容的空白而映射在DOM樹中的不必要的Text Node對象doc.
normalize();// //獲取jdbc
// NodeList jdbc = doc.
getElementsByTagName("jdbc");
// //只有一個jdbc,獲取jdbc中的驅動類的名稱
// NodeList driverClassNode =
((Element
)jdbc.
item(0
)).
getElementsByTagName("driver-class");
// String driverClass = driverClassNode.
item(0
).
getFirstChild().
getNodeValue();
// System.out.
println("driverClass=="+driverClass
);
// //同理獲取url、user、password等值
// NodeList urlNode =
((Element
)jdbc.
item(0
)).
getElementsByTagName("url");
// String url = urlNode.
item(0
).
getFirstChild().
getNodeValue();
// System.out.
println("url=="+url
);
//
// NodeList userNode =
((Element
)jdbc.
item(0
)).
getElementsByTagName("user");
// String user = userNode.
item(0
).
getFirstChild().
getNodeValue();
// System.out.
println("user=="+user
);
//
// NodeList passwordNode =
((Element
)jdbc.
item(0
)).
getElementsByTagName("password");
// String password = passwordNode.
item(0
).
getFirstChild().
getNodeValue();
// System.out.
println("password=="+password
);
// //獲取application-xml
// NodeList applicationXmlNode = doc.
getElementsByTagName("application-xml");
// String applicationXml = applicationXmlNode.
item(0
).
getFirstChild().
getNodeValue();
// System.out.
println("applicationXml=="+applicationXml
);//先要獲取spring-default,然后獲取application-xmls//然后才能獲取application-xml NodeList springDefaultNode = doc.
getElementsByTagName("spring-default");NodeList appXmlsNode =
((Element
)springDefaultNode.
item(0
)).
getElementsByTagName("application-xmls");NodeList appXmlNode =
((Element
)appXmlsNode.
item(0
)).
getElementsByTagName("application-xml");//循環獲取每個application-xml元素的值
for(int i=0
;i<appXmlNode.
getLength();i++){String applicationXml = appXmlNode.
item(i
).
getFirstChild().
getNodeValue();System.out.
println("applicationXml=="+applicationXml
);}}public static void main(String[] args) throws Exception {ReadAppXml t = new
ReadAppXml();t.
read("App2.xml");}
2.模式初步介紹
2.1 本質
解析器模式本質就是將"客戶端傳遞來的表達式"進行分解,分解成為一個一個的元素,并用一個對應的解析模型來組裝存儲。并將每個元素轉化成相對應的解析器對象,最后按照先后順序,把這些解析器對象組合起來,就得到抽象語法樹了(從而可以用程序的結構來對應表達式)
2.2 實現步驟
step1 :需要先設計一個簡單的表達式語言,在客戶端調用解析程序的時候,傳入用這個表達式語言描述的一個表達式,然后把這個表達式通過解析器的解析,形成一個抽象的語法樹。
step2:解析完成后,自動調用解釋器來解釋抽象語法樹,并執行每個節點所對應的功能,從而完成通用的xml解析。這樣一來,每次當xml結構發生了更改,也就是在客戶端調用的時候,傳入不同的表達式即可,整個解析xml過程的代碼都不需要再修改了。
- TerminalExpression:終結符解釋器,用來實現語法規則中和終結符相關的操作,不再包含其他的解釋器,如果用組合模式來構建抽象語法樹的話,就相當于組合模式中的葉子對象,可以有多種終結符解釋器。
- NonterminalExpression:非終結符解釋器,用來實現語法規則中非終結符相關的操作,通常一個解釋器對應一個語法規則,可以包含其他的解釋器,如果用組合模式來構建抽象語法樹的話,就相當于組合模式中的組合對象。可以有多種非終結符解釋器。
eg. 數據舉例
3. 應用模式解決
//上下文,用來包含解釋器需要的一些全局信息
public class Context {//上一個被處理的元素 private Element preEle = null
;//Dom解析Xml的Document對象 private Document document = null
; public Context(String filePathName) throws Exception{//通過輔助的Xml工具類來獲取被解析的xml對應的Document對象this.document = XmlUtil.
getRoot(filePathName
);} public void reInit(){preEle = null
;}public Element getNowEle(Element pEle,String eleName){NodeList tempNodeList = pEle.
getChildNodes();for(int i=0
;i<tempNodeList.
getLength();i++){if(tempNodeList.item(i) instanceof Element){Element nowEle =
(Element
)tempNodeList.
item(i
);if(nowEle.getTagName().equals(eleName)){return nowEle
;}}}return null
;} public Element getPreEle() {return preEle
;}public void setPreEle(Element preEle) {this.preEle = preEle
;} public Document getDocument() {return document
;}
}public abstract class ReadXmlExpression {// 解釋表達式, 為了通用,可能是單個值,也可能是多個值,//因此就返回一個數組 public abstract String[]
interpret(Context c
);
}//屬性解釋器
public class PropertyTerminalExpression extends ReadXmlExpression{//屬性的名字 private String propName
;public PropertyTerminalExpression(String propName){this.propName = propName
;}public String[] interpret(Context c) {//直接獲取最后的元素的屬性的值String[] ss = new String[1]
;ss[0] = c.
getPreEle().
getAttribute(this.propName
);return ss
;}
}//元素終結符解釋器
public class ElementTerminalExpression extends ReadXmlExpression{ private String eleName =
"";//元素的名字public ElementTerminalExpression(String name){this.eleName = name
;} public String[] interpret(Context c) {//先取出上下文里的當前元素作為父級元素Element pEle = c.
getPreEle();//查找到當前元素名稱所對應的xml元素Element ele = null
;if(pEle==null){//說明現在獲取的是根元素ele = c.
getDocument().
getDocumentElement();c.
setPreEle(ele
);}else{//根據父級元素和要查找的元素的名稱來獲取當前的元素ele = c.
getNowEle(pEle, eleName
);//把當前獲取的元素放到上下文里面c.
setPreEle(ele
);}//然后需要去獲取這個元素的值String[] ss = new String[1]
;ss[0] = ele.
getFirstChild().
getNodeValue();return ss
;}
}//元素非終結符解釋器,解釋并執行中間元素
public class ElementExpression extends ReadXmlExpression{//用來記錄組合的ReadXmlExpression元素 private Collection<ReadXmlExpression> eles = new ArrayList<ReadXmlExpression>
();//元素的名稱 private String eleName =
"";public ElementExpression(String eleName){this.eleName = eleName
;}public boolean addEle(ReadXmlExpression ele){this.eles.
add(ele
);return true
;}public boolean removeEle(ReadXmlExpression ele){this.eles.
remove(ele
);return true
;}public String[] interpret(Context c) {//先取出上下文里的當前元素作為父級元素//查找到當前元素名稱所對應的xml元素,并設置回到上下文中Element pEle = c.
getPreEle();if(pEle==null){//說明現在獲取的是根元素c.
setPreEle(c.
getDocument().
getDocumentElement());}else{//根據父級元素和要查找的元素的名稱來獲取當前的元素Element nowEle = c.
getNowEle(pEle, eleName
);//把當前獲取的元素放到上下文里面c.
setPreEle(nowEle
);}//循環調用子元素的interpret方法String [] ss = null
;for(ReadXmlExpression ele : eles){ss = ele.
interpret(c
);}return ss
;}
}public class XmlUtil {public static Document getRoot(String filePathName) throws Exception{Document doc = null
;//建立一個解析器工廠DocumentBuilderFactory factory = DocumentBuilderFactory.
newInstance();//獲得一個DocumentBuilder對象,這個對象代表了具體的DOM解析器DocumentBuilder builder=factory.
newDocumentBuilder();//得到一個表示XML文檔的Document對象doc=builder.
parse(filePathName
);//去掉XML文檔中作為格式化內容的空白而映射在DOM樹中的不必要的Text Node對象doc.
normalize();return doc
;}
}public static void main(String[] args) throws Exception {//準備上下文Context c = new
Context("InterpreterTest.xml");//想要獲取c元素的值,也就是如下表達式的值:
"root/a/b/c"
// //首先要構建解釋器的抽象語法樹
// ElementExpression root = new
ElementExpression("root");
// ElementExpression aEle = new
ElementExpression("a");
// ElementExpression bEle = new
ElementExpression("b");
// ElementTerminalExpression cEle = new
ElementTerminalExpression("c");
// //組合起來
// root.
addEle(aEle
);
// aEle.
addEle(bEle
);
// bEle.
addEle(cEle
);
//
// //調用
// String ss[] = root.
interpret(c
);
// System.out.
println("c的值是="+ss[0]
);//想要獲取c元素的name屬性,也就是如下表達式的值:
"root/a/b/c.name"//這個時候c不是終結了,需要把c修改成ElementExpressioinElementExpression root = new
ElementExpression("root");ElementExpression aEle = new
ElementExpression("a");ElementExpression bEle = new
ElementExpression("b");ElementExpression cEle = new
ElementExpression("c");PropertyTerminalExpression prop = new
PropertyTerminalExpression("name");//組合root.
addEle(aEle
);aEle.
addEle(bEle
);bEle.
addEle(cEle
);cEle.
addEle(prop
);//調用String ss[] = root.
interpret(c
);System.out.
println("c的屬性name的值是="+ss[0]
);//如果要使用同一個上下文,連續進行解析,需要重新初始化上下文對象//比如要連續的重新再獲取一次屬性name的值,當然你可以重新組合元素,//重新解析,只要是在使用同一個上下文,就需要重新初始化上下文對象c.
reInit();String ss2[] = root.
interpret(c
);System.out.
println("重新獲取c的屬性name的值是="+ss2[0]
);}
4. 總結
4. 1 解釋器模式核心
使用解釋器對象來表示和處理相應的語法規則,一般一個解釋器處理一條語法規則。理論上來說,只要能用解釋器對象把符合語法的表達式表示出來,而且能夠構成抽象的語法樹,那都可以使用解釋器模式來處理。
4. 2 語法規則和解釋器
語法規則和解釋器之間是有對應關系的,一般一個解釋器處理一條語法規則,但是反過來并不成立,一條語法規則是可以有多種解釋和處理的,也就是一條語法規則可以對應多個解釋器對象。
4. 3 上下文的公用性
上下文中存儲和訪問解釋器的狀態,比如,前面的解釋器可以存儲一些數據在上下文中,后面的解釋器就可以獲取這些值。另外還可以傳遞一些在解釋器外部,但是解釋器需要的數據,也可以是一些全局的、公共的數據。
上下文還有一個功能,就是可以提供所有解釋器對象的公共功能,類似于對象組合,而不是使用繼承來獲取公共功能,在每個解釋器對象中都可以調用。
4. 4 誰來構建抽象語法樹
后面會提供解析器來實現把表達式轉換成為抽象語法樹。
4. 5.誰負責解釋操作
只要定義好了抽象語法樹,肯定是解釋器來負責解釋執行。雖然有不同的語法規則,但是解釋器不負責選擇究竟用哪一個解釋器對象來解釋執行語法規則,選擇解釋器的功能在構建抽象語法樹的時候就完成了。
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的《研磨设计模式》chap21 解释器模式Interpreter(1)模式介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。