场景
通常项目中有各种类型的配置文件:xml、properties文件等,如果我们需要自己实现一个配置文件读取程序,解释器模式将会是好的选择。
栗子
模拟使用使用解释器解析xml
resources下config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<root id="rootId">
<a>
<b>
<c name="cName">c1</c>
</b>
</a>
</root>
解释器接口
/**
* 抽象表达式--解释器接口--
*/
public abstract class AbstractXMLExpression {
public abstract String[] interpret(Context context);
}
非终结表达式-- 例如 root/a/b/c a或b的位置
import org.w3c.dom.Element;
import java.util.ArrayList;
import java.util.List;
/**
* 元素表达式
*/
public class ElementExpression extends AbstractXMLExpression{
private List<AbstractXMLExpression> elements=new ArrayList<>();
//元素名称
private String elementName;
public ElementExpression(String elementName) {
this.elementName = elementName;
}
public boolean addEle(AbstractXMLExpression ele){
elements.add(ele);
return true;
}
public boolean removeEle(AbstractXMLExpression ele){
elements.remove(ele);
return true;
}
@Override
public String[] interpret(Context context) {
Element beforeEle = context.getBeforeEle();
if (beforeEle==null){
context.setBeforeEle(context.getDocument().getDocumentElement());
}else {
Element currentEle = context.getCurrentEle(beforeEle, elementName);
context.setBeforeEle(currentEle);
}
//循环调用解释
String[] ss=null;
for (AbstractXMLExpression element:elements) {
ss=element.interpret(context);
}
return ss;
}
}
元素终结表达式
import org.w3c.dom.Element;
/**
* 元素--结束表达式--结束符
*/
public class ElementEndExpression extends AbstractXMLExpression{
private String elementName;
public ElementEndExpression(String elementName) {
this.elementName = elementName;
}
@Override
public String[] interpret(Context context) {
Element beforeEle = context.getBeforeEle();
Element ele;
if (beforeEle==null){
ele=context.getDocument().getDocumentElement();
context.setBeforeEle(ele);
}else {
ele = context.getCurrentEle(beforeEle, elementName);
context.setBeforeEle(ele);
}
//循环调用解释
String[] ss=new String[1];
ss[0]=ele.getFirstChild().getNodeValue();
return ss;
}
}
属性终结表达式
/**
*元素属性表达式--结束符
*/
public class ElementPropertyEndExpression extends AbstractXMLExpression{
private String propName;
public ElementPropertyEndExpression(String propName) {
this.propName = propName;
}
@Override
public String[] interpret(Context context) {
String[] ss=new String[1];
ss[0]=context.getBeforeEle().getAttribute(this.propName);
return ss;
}
}
XML工具类
public class XmlUtil {
public static Document getRoot(String filePath) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = factory.newDocumentBuilder();
Document document = documentBuilder.parse(filePath);
//格式化
document.normalize();
return document;
}
}
上下文对象
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* 上下文--定义全局信息
* 1.获取单元素的值
* 根元素前不加"/",例如"root/a/b/c"表示获取a、b元素下的c元素的值。
* 2.获取单个元素属性的值。root/a/b/c.name
*/
public class Context {
private Element beforeEle;
//it is the root of the document tree
private Document document;
public Context(String filePath) throws Exception {
this.document = XmlUtil.getRoot(filePath);
}
/**
* 初始化上个元素
*/
public void reInit() {
beforeEle = null;
}
/**
* 获取当前元素
* @param beforeEle
* @param elmentName
* @return
*/
public Element getCurrentEle(Element beforeEle, String elmentName) {
NodeList childNodes = beforeEle.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
boolean flag = childNodes.item(i) instanceof Element;
if (!flag) {
continue;
}
Element currentEle = (Element) childNodes.item(i);
if (currentEle.getTagName().equals(elmentName)) {
return currentEle;
}
}
return null;
}
public void setBeforeEle(Element beforeEle) {
this.beforeEle = beforeEle;
}
/**
* 获取上一个元素
* @return
*/
public Element getBeforeEle() {
return beforeEle;
}
public Document getDocument() {
return document;
}
}
测试
@Test
void interpretTest() throws Exception {
String file = ClassLoader.getSystemClassLoader().getResource("conf.xml").getFile();
Context context = new Context(file);
//构建抽象语法树
//---root/a/b/c
ElementExpression root = new ElementExpression("root");
ElementExpression a = new ElementExpression("a");
ElementExpression b = new ElementExpression("b");
ElementEndExpression c = new ElementEndExpression("c"); root.addEle(a);
a.addEle(b);
b.addEle(c);
String[] ss = root.interpret(context);
System.out.println("标签c的值是:"+ss[0]);
//---root/a/b/c.name
context.reInit();
ElementExpression root2 = new ElementExpression("root");
ElementExpression a2 = new ElementExpression("a");
ElementExpression b2 = new ElementExpression("b");
ElementExpression c2 = new ElementExpression("c");
ElementPropertyEndExpression cname = new ElementPropertyEndExpression("name");
root2.addEle(a2);
a2.addEle(b2);
b2.addEle(c2);
c2.addEle(cname);
String[] ss2 = root2.interpret(context);
System.out.println("标签c的name属性是:"+ss2[0]);
}
测试结果
标签c的值是:c1
标签c的name属性是:cName
总结
1.定义:定义解释规则和解释器,解释器使用解释规则解释语言的句子。
2.解释器模式是将解析器封装的抽象语法树做功能解释。
3.配置文件的解析流程为:解析器将客户端传递的表达式(root/a/b/c)解析为抽象语法树,解释器将抽象语法树解释为具体功能。
4.场景:语言需要解释,比如mybatis配置文件。适用于语法简单、效率不高的场景。