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

相关文章

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

SpringBoot如何通过Map实现策略模式

《SpringBoot如何通过Map实现策略模式》策略模式是一种行为设计模式,它允许在运行时选择算法的行为,在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式,这... 目录前言底层机制解析Spring的集合类型自动装配@Resource注解的行为实现原理使用直接使用M

MySQL中慢SQL优化的不同方式介绍

《MySQL中慢SQL优化的不同方式介绍》慢SQL的优化,主要从两个方面考虑,SQL语句本身的优化,以及数据库设计的优化,下面小编就来给大家介绍一下有哪些方式可以优化慢SQL吧... 目录避免不必要的列分页优化索引优化JOIN 的优化排序优化UNION 优化慢 SQL 的优化,主要从两个方面考虑,SQL 语

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程

Python实现html转png的完美方案介绍

《Python实现html转png的完美方案介绍》这篇文章主要为大家详细介绍了如何使用Python实现html转png功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 1.增强稳定性与错误处理建议使用三层异常捕获结构:try: with sync_playwright(

Java使用多线程处理未知任务数的方案介绍

《Java使用多线程处理未知任务数的方案介绍》这篇文章主要为大家详细介绍了Java如何使用多线程实现处理未知任务数,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 知道任务个数,你可以定义好线程数规则,生成线程数去跑代码说明:1.虚拟线程池:使用 Executors.newVir

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

大数据spark3.5安装部署之local模式详解

《大数据spark3.5安装部署之local模式详解》本文介绍了如何在本地模式下安装和配置Spark,并展示了如何使用SparkShell进行基本的数据处理操作,同时,还介绍了如何通过Spark-su... 目录下载上传解压配置jdk解压配置环境变量启动查看交互操作命令行提交应用spark,一个数据处理框架

JAVA SE包装类和泛型详细介绍及说明方法

《JAVASE包装类和泛型详细介绍及说明方法》:本文主要介绍JAVASE包装类和泛型的相关资料,包括基本数据类型与包装类的对应关系,以及装箱和拆箱的概念,并重点讲解了自动装箱和自动拆箱的机制,文... 目录1. 包装类1.1 基本数据类型和对应的包装类1.2 装箱和拆箱1.3 自动装箱和自动拆箱2. 泛型2

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

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