热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android的设计模式解释器模式

前言Android的设计模式系列文章介绍,欢迎关注,持续更新中:Android的设计模式-设计模式的六大原则创建型模式:A
前言

Android的设计模式系列文章介绍,欢迎关注,持续更新中:

Android的设计模式-设计模式的六大原则
创建型模式:
Android的设计模式-单例模式
Android的设计模式-建造者模式
Android的设计模式-工厂方法模式
Android的设计模式-简单工厂模式
Android的设计模式-抽象工厂模式
Android的设计模式-原型模式
行为型模式:
Android的设计模式-策略模式
Android的设计模式-状态模式
Android的设计模式-责任链模式
Android的设计模式-观察者模式
Android的设计模式-模板方法模式
Android的设计模式-迭代器模式
Android的设计模式-备忘录模式
Android的设计模式-访问者模式
Android的设计模式-中介者模式
Android的设计模式-解释器模式


1.定义

给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。

2.介绍


  • 解释器模式属于行为型模式。
  • 解释器模式提供了一种解释语言的语法或表达式的方式。
  • 解释器模式实际开发中很少用到。

3.UML类图

解释器模式UML类图.jpg

角色说明:

  • AbstractExpression(抽象表达式):定义一个抽象的解释方法,其具体的实现在各个具体的子类解释器中完成。
  • TerminalExpression(终结符表达式):实现对文法中与终结符有关的解释操作。
  • NonterminalExpression(非终结符表达式):实现对文法中的非终结符有关的解释操作。
  • Context(环境角色):包含解释器之外的全部信息。
  • Client(客户端角色):解析表达式,构建抽象语法树,执行具体的解释操作等。

4.实现

以加减法的实现为例,我们实现下面表达式的解释并输出结果,为了方便解释,在表达式中介加了空格方便处理。

a = 1024
b = 512
a + b
a - b

4.1 创建抽象表达式

public abstract class ArithmeticExpression {//抽象算术表达式public abstract Object interpret(Context context);//抽象解释方法}

4.2 终结符表达式

从上面的表达式可以看出,终结符有两种,一种是数字,另外一种是变量。

//数字表达式,用来解释数字public class NumExpression extends ArithmeticExpression {private String strNum;public NumExpression(String strNum) {this.strNum = strNum;}@Overridepublic Integer interpret(Context context) {//解释数字return Integer.parseInt(strNum);}}//变量表达式,用来解释变量class VarExpression extends ArithmeticExpression {private String var;public VarExpression(String var) {this.var = var;}@Overridepublic String interpret(Context context) {//解释变量return var;}}

4.3 创建非终结符表达式

上面的表达式有三种非终结符,分别是+号、-号和=号。

//加法表达式,用来解释加法,如a+bpublic class AddExpression extends ArithmeticExpression {private ArithmeticExpression left, right;//加号左右两边的内容public AddExpression(ArithmeticExpression left, ArithmeticExpression right) {this.left = left;this.right = right;}@Overridepublic Integer interpret(Context context) {//解释加法表达式的结果,即算出left+right的结果return context.get((String) left.interpret(context)) + context.get((String) right.interpret(context));}}//减法表达式,用来解释减法,如a-bpublic class SubExpression extends ArithmeticExpression {private ArithmeticExpression left, right;//减号左右两边的内容public SubExpression(ArithmeticExpression left, ArithmeticExpression right) {this.left = left;this.right = right;}@Overridepublic Integer interpret(Context context) {//解释减法表达式的结果,即算出left-right的结果return context.get((String) left.interpret(context)) - context.get((String) right.interpret(context));}}//等号表达式,用来解释变量赋值,如a=1024public class EqualExpression extends ArithmeticExpression {private ArithmeticExpression left, right;//等号左右两边的内容public EqualExpression(ArithmeticExpression left, ArithmeticExpression right) {this.left = left;this.right = right;}@Overridepublic Object interpret(Context context) {//解释等号表达式的结果,并将结果保存到context,变量名为key,值为valuecontext.put((String) left.interpret(context), (int) right.interpret(context));return null;}}

4.4 创建环境角色

创建环境主要包含解释器之外的全部信息,这里用来保存变量以及其值。

public class Context {Map<String, Object> mMap &#61; new HashMap<>();//使用HashMap来保存结果public void put(String key, int value) {mMap.put(key, value);}public int get(String key) {return (int) mMap.get(key);}}

4.5 创建客户端角色&#xff1a;

客户端角色主要负责解析表达式&#xff0c;构建抽象语法树&#xff0c;执行具体的解释操作等。

public class Calculator {//计算器类Context mContext &#61; new Context();private ArithmeticExpression mExpression;public void read(String expression) {//读取表达式String[] split &#61; expression.split(" ");//表达式以空格隔开&#xff0c;方便拆分switch (split[1]) {//根据不同符号去执行具体的解析操作case "&#61;":new EqualExpression(new VarExpression(split[0]), new NumExpression(split[2])).interpret(mContext);break;case "&#43;":mExpression &#61; new AddExpression(new VarExpression(split[0]), new VarExpression(split[2]));break;case "-":mExpression &#61; new SubExpression(new VarExpression(split[0]), new VarExpression(split[2]));break;}}public int calculate() {//计算结果return (int) mExpression.interpret(mContext);}}

4.6 客户端测试&#xff1a;

public void test() {Calculator calculator &#61; new Calculator();calculator.read("a &#61; 1024");//读取表达式calculator.read("b &#61; 512");System.out.println("a &#61; 1024");System.out.println("b &#61; 512");calculator.read("a &#43; b");System.out.println("a &#43; b &#61; " &#43; calculator.calculate());//计算结果calculator.read("a - b");System.out.println("a - b &#61; " &#43; calculator.calculate());}

输出结果&#xff1a;

a &#61; 1024
b &#61; 512
a &#43; b &#61; 1536
a - b &#61; 512

5. 应用场景


  • 简单的语法需要解释时&#xff0c;如解释一个sql语句。
  • 一些重复发生的问题&#xff0c;比如加减乘除四则运算&#xff0c;但是公式每次都不同&#xff0c;有时是a&#43;b-cd&#xff0c;有时是ab&#43;c-d等&#xff0c;公式千变万化&#xff0c;但是都是由加减乘除四个非终结符来连接的&#xff0c;这时我们就可以使用解释器模式。

6. 优点


  • 灵活的扩展性&#xff0c;想扩展语法规则时只需新增新的解释器就可以了。如上面的例子中&#xff0c;想增加乘除法&#xff0c;只想增加相应的解释类&#xff0c;并增加相应的表达式解释操作即可。

7. 缺点


  • 每一个文法都至少对应一个解释器&#xff0c;会产生大量的类&#xff0c;难于维护。
  • 解释器模式由于大量使用循环和递归&#xff0c;需要考虑效率的问题&#xff0c;而且调试也不方便。
  • 对于复杂的文法&#xff0c;构建其抽象语法树会显得异常繁琐。
  • 所以不推荐在重要的模块中使用解释器模式&#xff0c;维护困难。

8. Android中的源码分析

对于AndroidManifest.xml这个文件&#xff0c;我们是相当熟悉。实际上AndroidManifest.xml是由PackageManagerService使用了PackageParser这个类来解释的&#xff0c;这里面就用到了解释器模式。对于AndroidManifest.xml中的每一个标签&#xff0c;都有对应的类去保存相应的信息。

8.1 PackageParser的parseBaseApkCommon方法

基于Android 27的源码&#xff0c;不同版本的源码方法名可能不一样。

private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException {//其他代码略if (tagName.equals(TAG_APPLICATION)) {//其他代码略if (!parseBaseApplication(pkg, res, parser, flags, outError)) {//解释application标签return null;}} else if (tagName.equals(TAG_OVERLAY)) {//其他代码略} else if (tagName.equals(TAG_KEY_SETS)) {if (!parseKeySets(pkg, res, parser, outError)) {return null;}} else if (tagName.equals(TAG_PERMISSION_GROUP)) {if (!parsePermissionGroup(pkg, flags, res, parser, outError)) {return null;}} else if (tagName.equals(TAG_PERMISSION)) {if (!parsePermission(pkg, res, parser, outError)) {return null;}} else if (tagName.equals(TAG_PERMISSION_TREE)) {if (!parsePermissionTree(pkg, res, parser, outError)) {return null;}} else if (tagName.equals(TAG_USES_PERMISSION)) {if (!parseUsesPermission(pkg, res, parser)) {return null;}} else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)|| tagName.equals(TAG_USES_PERMISSION_SDK_23)) {if (!parseUsesPermission(pkg, res, parser)) {return null;}} else if (tagName.equals(TAG_USES_CONFIGURATION)) {//其他代码略} else if (tagName.equals(TAG_USES_FEATURE)) {//其他代码略} else if (tagName.equals(TAG_FEATURE_GROUP)) {//其他代码略} else if (tagName.equals(TAG_USES_SDK)) {//其他代码略} else if (tagName.equals(TAG_SUPPORT_SCREENS)) {//其他代码略} else if (tagName.equals(TAG_PROTECTED_BROADCAST)) {//其他代码略} else if (tagName.equals(TAG_INSTRUMENTATION)) {//其他代码略} else if (tagName.equals(TAG_ORIGINAL_PACKAGE)) {//其他代码略} else if (tagName.equals(TAG_ADOPT_PERMISSIONS)) {//其他代码略} else if (tagName.equals(TAG_USES_GL_TEXTURE)) {//其他代码略} else if (tagName.equals(TAG_COMPATIBLE_SCREENS)) {//其他代码略} else if (tagName.equals(TAG_SUPPORTS_INPUT)) {////其他代码略} else if (tagName.equals(TAG_EAT_COMMENT)) {//其他代码略} else if (tagName.equals(TAG_PACKAGE)) {//其他代码略} else if (tagName.equals(TAG_RESTRICT_UPDATE)) {//其他代码略} else if (RIGID_PARSER) {//其他代码略} else {//其他代码略}}

从上面代码可以看到&#xff0c;就是对各个标签的内容进行解释。我们再来看看parseBaseApplication这个方法&#xff0c;这个是对Application进行解释。

8.2 parseBaseApplication方法

private boolean parseBaseApplication(Package owner, Resources res,XmlResourceParser parser, int flags, String[] outError)throws XmlPullParserException, IOException {//其他代码略String tagName &#61; parser.getName();if (tagName.equals("activity")) {//解释activityActivity a &#61; parseActivity(owner, res, parser, flags, outError, cachedArgs, false,owner.baseHardwareAccelerated);//其他代码略} else if (tagName.equals("receiver")) {//解释receiverActivity a &#61; parseActivity(owner, res, parser, flags, outError, cachedArgs,true, false);//其他代码略} else if (tagName.equals("service")) {//解释serviceService s &#61; parseService(owner, res, parser, flags, outError, cachedArgs);//其他代码略} else if (tagName.equals("provider")) {//解释providerProvider p &#61; parseProvider(owner, res, parser, flags, outError, cachedArgs);//其他代码略} else if (tagName.equals("activity-alias")) {Activity a &#61; parseActivityAlias(owner, res, parser, flags, outError, cachedArgs);//其他代码略} else if (parser.getName().equals("meta-data")) {//其他代码略} else if (tagName.equals("static-library")) {//其他代码略} else if (tagName.equals("library")) {//其他代码略} else if (tagName.equals("uses-static-library")) {if (!parseUsesStaticLibrary(owner, res, parser, outError)) {return false;}} else if (tagName.equals("uses-library")) {//其他代码略} else if (tagName.equals("uses-package")) {//其他代码略} else {//其他代码略}//其他代码略return true;}

可以看到&#xff0c;上面有对activityreceiverservice等标签的解释&#xff0c;activity的具体解释在parseActivity这个方法里面&#xff0c;有兴趣的可以自行去看下&#xff0c;这里就不细说了&#xff0c;同时可以看到receiver也是在parseActivity这个方法中解释。

相关文章阅读
Android的设计模式-设计模式的六大原则
创建型模式&#xff1a;
Android的设计模式-单例模式
Android的设计模式-建造者模式
Android的设计模式-工厂方法模式
Android的设计模式-简单工厂模式
Android的设计模式-抽象工厂模式
Android的设计模式-原型模式
行为型模式&#xff1a;
Android的设计模式-策略模式
Android的设计模式-状态模式
Android的设计模式-责任链模式
Android的设计模式-观察者模式
Android的设计模式-模板方法模式
Android的设计模式-迭代器模式
Android的设计模式-备忘录模式
Android的设计模式-访问者模式
Android的设计模式-中介者模式
Android的设计模式-解释器模式


推荐阅读
  • RecyclerView初步学习(一)
    RecyclerView初步学习(一)ReCyclerView提供了一种插件式的编程模式,除了提供ViewHolder缓存模式,还可以自定义动画,分割符,布局样式,相比于传统的ListVi ... [详细]
  • 探讨如何真正掌握Java EE,包括所需技能、工具和实践经验。资深软件教学总监李刚分享了对毕业生简历中常见问题的看法,并提供了详尽的标准。 ... [详细]
  • 深入解析Spring启动过程
    本文详细介绍了Spring框架的启动流程,帮助开发者理解其内部机制。通过具体示例和代码片段,解释了Bean定义、工厂类、读取器以及条件评估等关键概念,使读者能够更全面地掌握Spring的初始化过程。 ... [详细]
  • Microsoft即将发布WPF/E的CTP(Community Technology Preview)和SDK,标志着RIA(Rich Internet Application)技术的新里程碑。更多详情及下载链接请参见MSDN官方页面。 ... [详细]
  • 本文详细介绍了Oracle数据库中审计日志(audit trail)的配置方法及各参数选项的功能,包括如何启用系统范围的审计记录,以及如何将审计数据存储在不同的位置和格式。 ... [详细]
  • 本文介绍如何通过Windows批处理脚本定期检查并重启Java应用程序,确保其持续稳定运行。脚本每30分钟检查一次,并在需要时重启Java程序。同时,它会将任务结果发送到Redis。 ... [详细]
  • 本章将深入探讨移动 UI 设计的核心原则,帮助开发者构建简洁、高效且用户友好的界面。通过学习设计规则和用户体验优化技巧,您将能够创建出既美观又实用的移动应用。 ... [详细]
  • 创建项目:Visual Studio Online 入门指南
    本文介绍如何使用微软的 Visual Studio Online(VSO)创建和管理开发项目。作为一款基于云计算的开发平台,VSO 提供了丰富的工具和服务,简化了项目的配置和部署流程。 ... [详细]
  • 实用正则表达式有哪些
    小编给大家分享一下实用正则表达式有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下 ... [详细]
  • 本文介绍如何从字符串中移除大写、小写、特殊、数字和非数字字符,并提供了多种编程语言的实现示例。 ... [详细]
  • 本文探讨了如何在 Pug 模板中正确地使用 JSON 插值,并解决了相关文档不足的问题。我们将介绍通过 gulp-pug 处理 JSON 数据的具体方法,以及如何在模板中插入和显示这些数据。 ... [详细]
  • 俗话说得好,“工欲善其事,必先利其器”。这句话不仅强调了工具的重要性,也提醒我们在任何项目开始前,准备合适的工具至关重要。本文将介绍几款C语言编程中常用的工具,帮助初学者更好地选择适合自己学习和工作的编程环境。 ... [详细]
  • Java 架构:深入理解 JDK 动态代理机制
    代理模式是 Java 中常用的设计模式之一,其核心在于代理类与委托类共享相同的接口。代理类主要用于为委托类提供预处理、过滤、转发及后处理等功能,以增强或改变原有功能的行为。 ... [详细]
  • 本教程将深入探讨C#编程语言中的条件控制结构,包括if语句和switch语句的使用方法。通过本课的学习,您将掌握如何利用这些控制结构来实现程序的条件分支逻辑。 ... [详细]
  • 本文介绍了如何在Linode服务器上以root用户身份安装Xubuntu,并解决尝试启动图形界面时遇到的'无屏幕找到'错误。 ... [详细]
author-avatar
ZZDXP
学 無
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有