Android的设计模式-解释器模式

前言

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

Android的设计模式-设计模式的六大原则
一句话总结23种设计模式则
创建型模式:
Android的设计模式-单例模式
Android的设计模式-建造者模式
Android的设计模式-工厂方法模式
Android的设计模式-简单工厂模式
Android的设计模式-抽象工厂模式
Android的设计模式-原型模式
行为型模式:
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;
        }

        @Override
        public Integer interpret(Context context) {//解释数字
            return Integer.parseInt(strNum);
        }
    }

    //变量表达式,用来解释变量
    class VarExpression extends ArithmeticExpression {
        private String var;

        public VarExpression(String var) {
            this.var = var;
        }

        @Override
        public String interpret(Context context) {//解释变量
            return var;
        }
    }
4.3 创建非终结符表达式

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

     //加法表达式,用来解释加法,如a+b
    public class AddExpression extends ArithmeticExpression {
        private ArithmeticExpression left, right;//加号左右两边的内容

        public AddExpression(ArithmeticExpression left, ArithmeticExpression right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public Integer interpret(Context context) {//解释加法表达式的结果,即算出left+right的结果
            return  context.get((String) left.interpret(context)) + context.get((String) right.interpret(context));
        }
    }

    //减法表达式,用来解释减法,如a-b
    public class SubExpression extends ArithmeticExpression {
        private ArithmeticExpression left, right;//减号左右两边的内容

        public SubExpression(ArithmeticExpression left, ArithmeticExpression right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public Integer interpret(Context context) {//解释减法表达式的结果,即算出left-right的结果
            return context.get((String) left.interpret(context)) - context.get((String) right.interpret(context));
        }
    }
    
    //等号表达式,用来解释变量赋值,如a=1024
    public class EqualExpression extends ArithmeticExpression {
        private ArithmeticExpression left, right;//等号左右两边的内容

        public EqualExpression(ArithmeticExpression left, ArithmeticExpression right) {
            this.left = left;
            this.right = right;
        }


        @Override
        public Object interpret(Context context) {//解释等号表达式的结果,并将结果保存到context,变量名为key,值为value
            context.put((String) left.interpret(context), (int) right.interpret(context));
            return null;
        }
    }
4.4 创建环境角色

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

    public class Context {
        Map<String, Object> mMap = 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 创建客户端角色:

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

     public class Calculator {//计算器类
        Context mContext = new Context();
        private ArithmeticExpression mExpression;

        public void read(String expression) {//读取表达式
            String[] split = expression.split(" ");//表达式以空格隔开,方便拆分
            switch (split[1]) {//根据不同符号去执行具体的解析操作
                case "=":
                    new EqualExpression(new VarExpression(split[0]), new NumExpression(split[2])).interpret(mContext);
                    break;
                case "+":
                    mExpression = new AddExpression(new VarExpression(split[0]), new VarExpression(split[2]));
                    break;
                case "-":
                    mExpression = new SubExpression(new VarExpression(split[0]), new VarExpression(split[2]));
                    break;
            }

        }

        public int calculate() {//计算结果
            return (int) mExpression.interpret(mContext);
        }
    }
4.6 客户端测试:
     public void test() {
        Calculator calculator = new Calculator();
        calculator.read("a = 1024");//读取表达式
        calculator.read("b = 512");
        System.out.println("a = 1024");
        System.out.println("b = 512");

        calculator.read("a + b");
        System.out.println("a + b = " + calculator.calculate());//计算结果
        calculator.read("a - b");
        System.out.println("a - b = " + calculator.calculate());
    }
输出结果:
a = 1024
b = 512
a + b = 1536
a - b = 512

5. 应用场景

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

6. 优点

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

7. 缺点

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

8. Android中的源码分析

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

8.1 PackageParser的parseBaseApkCommon方法

基于Android 27的源码,不同版本的源码方法名可能不一样。

    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 {
            //其他代码略
        }
    }

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

8.2 parseBaseApplication方法

    private boolean parseBaseApplication(Package owner, Resources res,
                                         XmlResourceParser parser, int flags, String[] outError)
            throws XmlPullParserException, IOException {

        //其他代码略
        String tagName = parser.getName();
        if (tagName.equals("activity")) {//解释activity
            Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
                    owner.baseHardwareAccelerated);
            //其他代码略

        } else if (tagName.equals("receiver")) {//解释receiver
            Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
                    true, false);
            //其他代码略

        } else if (tagName.equals("service")) {//解释service
            Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
            //其他代码略

        } else if (tagName.equals("provider")) {//解释provider
            Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
            //其他代码略

        } else if (tagName.equals("activity-alias")) {
            Activity a = 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;
    }

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

相关文章阅读
Android的设计模式-设计模式的六大原则
一句话总结23种设计模式则
创建型模式:
Android的设计模式-单例模式
Android的设计模式-建造者模式
Android的设计模式-工厂方法模式
Android的设计模式-简单工厂模式
Android的设计模式-抽象工厂模式
Android的设计模式-原型模式
行为型模式:
Android的设计模式-策略模式
Android的设计模式-状态模式
Android的设计模式-责任链模式
Android的设计模式-观察者模式
Android的设计模式-模板方法模式
Android的设计模式-迭代器模式
Android的设计模式-备忘录模式
Android的设计模式-访问者模式
Android的设计模式-中介者模式
Android的设计模式-解释器模式
Android的设计模式-命令模式
结构型模式:
Android的设计模式-代理模式
Android的设计模式-组合模式
Android的设计模式-适配器模式
Android的设计模式-装饰者模式
Android的设计模式-享元模式
Android的设计模式-外观模式
Android的设计模式-桥接模式

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343