《研磨设计模式》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

相关文章

Java实现状态模式的示例代码

《Java实现状态模式的示例代码》状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来... 目录一、简介1、定义2、状态模式的结构二、Java实现案例1、电灯开关状态案例2、番茄工作法状态案例

四种Flutter子页面向父组件传递数据的方法介绍

《四种Flutter子页面向父组件传递数据的方法介绍》在Flutter中,如果父组件需要调用子组件的方法,可以通过常用的四种方式实现,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录方法 1:使用 GlobalKey 和 State 调用子组件方法方法 2:通过回调函数(Callb

Python进阶之Excel基本操作介绍

《Python进阶之Excel基本操作介绍》在现实中,很多工作都需要与数据打交道,Excel作为常用的数据处理工具,一直备受人们的青睐,本文主要为大家介绍了一些Python中Excel的基本操作,希望... 目录概述写入使用 xlwt使用 XlsxWriter读取修改概述在现实中,很多工作都需要与数据打交

java脚本使用不同版本jdk的说明介绍

《java脚本使用不同版本jdk的说明介绍》本文介绍了在Java中执行JavaScript脚本的几种方式,包括使用ScriptEngine、Nashorn和GraalVM,ScriptEngine适用... 目录Java脚本使用不同版本jdk的说明1.使用ScriptEngine执行javascript2.

Python实现NLP的完整流程介绍

《Python实现NLP的完整流程介绍》这篇文章主要为大家详细介绍了Python实现NLP的完整流程,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 编程安装和导入必要的库2. 文本数据准备3. 文本预处理3.1 小写化3.2 分词(Tokenizatio

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型,并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络         谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络:谱图卷积网络、切比雪夫网络和图卷积网络。 (1)谱图卷积网络 卷积定理:函数卷积的傅里叶变换是函数傅里叶变换的乘积,即F{f*g}