《研磨设计模式》chap21 解释器模式Interpreter(1)模式介绍

2024-03-28 09:58

本文主要是介绍《研磨设计模式》chap21 解释器模式Interpreter(1)模式介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

场景:读写xml文件,如果代码"写死了":谁是谁的child,万一文件父子节点改了,又要改代码。

1. 正常编码(不使用模式)

public class ReadAppXml {/*** 读取配置文件内容* @param filePathName 配置文件的路径和文件名* @throws Exception*/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;}/*** 各个Expression公共使用的方法,* 根据父元素和当前元素的名称来获取当前的元素* @param pEle 父元素* @param eleName 当前元素的名称* @return 找到的当前元素*/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.谁负责解释操作

只要定义好了抽象语法树,肯定是解释器来负责解释执行。虽然有不同的语法规则,但是解释器不负责选择究竟用哪一个解释器对象来解释执行语法规则,选择解释器的功能在构建抽象语法树的时候就完成了。

这篇关于《研磨设计模式》chap21 解释器模式Interpreter(1)模式介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/855280

相关文章

zookeeper端口说明及介绍

《zookeeper端口说明及介绍》:本文主要介绍zookeeper端口说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、zookeeper有三个端口(可以修改)aVNMqvZ二、3个端口的作用三、部署时注意总China编程结一、zookeeper有三个端口(可以

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

Python中win32包的安装及常见用途介绍

《Python中win32包的安装及常见用途介绍》在Windows环境下,PythonWin32模块通常随Python安装包一起安装,:本文主要介绍Python中win32包的安装及常见用途的相关... 目录前言主要组件安装方法常见用途1. 操作Windows注册表2. 操作Windows服务3. 窗口操作

c++中的set容器介绍及操作大全

《c++中的set容器介绍及操作大全》:本文主要介绍c++中的set容器介绍及操作大全,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录​​一、核心特性​​️ ​​二、基本操作​​​​1. 初始化与赋值​​​​2. 增删查操作​​​​3. 遍历方

HTML img标签和超链接标签详细介绍

《HTMLimg标签和超链接标签详细介绍》:本文主要介绍了HTML中img标签的使用,包括src属性(指定图片路径)、相对/绝对路径区别、alt替代文本、title提示、宽高控制及边框设置等,详细内容请阅读本文,希望能对你有所帮助... 目录img 标签src 属性alt 属性title 属性width/h

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

MybatisPlus service接口功能介绍

《MybatisPlusservice接口功能介绍》:本文主要介绍MybatisPlusservice接口功能介绍,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友... 目录Service接口基本用法进阶用法总结:Lambda方法Service接口基本用法MyBATisP

MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)

《MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)》掌握多表联查(INNERJOIN,LEFTJOIN,RIGHTJOIN,FULLJOIN)和子查询(标量、列、行、表子查询、相关/非相关、... 目录第一部分:多表联查 (JOIN Operations)1. 连接的类型 (JOIN Types)

java中BigDecimal里面的subtract函数介绍及实现方法

《java中BigDecimal里面的subtract函数介绍及实现方法》在Java中实现减法操作需要根据数据类型选择不同方法,主要分为数值型减法和字符串减法两种场景,本文给大家介绍java中BigD... 目录Java中BigDecimal里面的subtract函数的意思?一、数值型减法(高精度计算)1.