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

相关文章

SQL Server身份验证模式步骤和示例代码

《SQLServer身份验证模式步骤和示例代码》SQLServer是一个广泛使用的关系数据库管理系统,通常使用两种身份验证模式:Windows身份验证和SQLServer身份验证,本文将详细介绍身份... 目录身份验证方式的概念更改身份验证方式的步骤方法一:使用SQL Server Management S

Java中 instanceof 的用法详细介绍

《Java中instanceof的用法详细介绍》在Java中,instanceof是一个二元运算符(类型比较操作符),用于检查一个对象是否是某个特定类、接口的实例,或者是否是其子类的实例,这篇文章... 目录引言基本语法基本作用1. 检查对象是否是指定类的实例2. 检查对象是否是子类的实例3. 检查对象是否

Redis高可用-主从复制、哨兵模式与集群模式详解

《Redis高可用-主从复制、哨兵模式与集群模式详解》:本文主要介绍Redis高可用-主从复制、哨兵模式与集群模式的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录Redis高可用-主从复制、哨兵模式与集群模式概要一、主从复制(Master-Slave Repli

什么是ReFS 文件系统? ntfs和refs的优缺点区别介绍

《什么是ReFS文件系统?ntfs和refs的优缺点区别介绍》最近有用户在Win11Insider的安装界面中发现,可以使用ReFS来格式化硬盘,这是不是意味着,ReFS有望在未来成为W... 数十年以来,Windows 系统一直将 NTFS 作为「内置硬盘」的默认文件系统。不过近些年来,微软还在研发一款名

一文带你搞懂Redis Stream的6种消息处理模式

《一文带你搞懂RedisStream的6种消息处理模式》Redis5.0版本引入的Stream数据类型,为Redis生态带来了强大而灵活的消息队列功能,本文将为大家详细介绍RedisStream的6... 目录1. 简单消费模式(Simple Consumption)基本概念核心命令实现示例使用场景优缺点2

C#使用StackExchange.Redis实现分布式锁的两种方式介绍

《C#使用StackExchange.Redis实现分布式锁的两种方式介绍》分布式锁在集群的架构中发挥着重要的作用,:本文主要介绍C#使用StackExchange.Redis实现分布式锁的... 目录自定义分布式锁获取锁释放锁自动续期StackExchange.Redis分布式锁获取锁释放锁自动续期分布式

Nginx location匹配模式与规则详解

《Nginxlocation匹配模式与规则详解》:本文主要介绍Nginxlocation匹配模式与规则,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、环境二、匹配模式1. 精准模式2. 前缀模式(不继续匹配正则)3. 前缀模式(继续匹配正则)4. 正则模式(大

redis过期key的删除策略介绍

《redis过期key的删除策略介绍》:本文主要介绍redis过期key的删除策略,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录第一种策略:被动删除第二种策略:定期删除第三种策略:强制删除关于big key的清理UNLINK命令FLUSHALL/FLUSHDB命

Pytest多环境切换的常见方法介绍

《Pytest多环境切换的常见方法介绍》Pytest作为自动化测试的主力框架,如何实现本地、测试、预发、生产环境的灵活切换,本文总结了通过pytest框架实现自由环境切换的几种方法,大家可以根据需要进... 目录1.pytest-base-url2.hooks函数3.yml和fixture结论你是否也遇到过

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

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