Java设计模式设计模式

《设计模式》解释器模式

2019-08-19  本文已影响0人  敏捷Studio

定义

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

介绍

UML类图

解释器模式UML类图

角色说明:

实现

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

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

1、创建抽象表达式

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

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;
  }
}

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

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

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

  // 解释加法表达式的结果,即算出left+right的结果
  @Override
  public Integer interpret(Context context) {
    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;
  }

  // 解释减法表达式的结果,即算出left-right的结果
  @Override
  public Integer interpret(Context context) {
    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;
  }

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

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

public class Context {
  // 使用HashMap来保存结果
  Map<String, Object> mMap = new HashMap<>();

  public void put(String key, int value) {
    mMap.put(key, value);
  }

  public int get(String key) {
    return (int) mMap.get(key);
  }
}

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

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

  // 读取表达式
  public void read(String expression) {
    // 表达式以空格隔开,方便拆分
    String[] split = expression.split(" ");
    // 根据不同符号去执行具体的解析操作
    switch (split[1]) {
      case "=":
        mExpression = 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);
  }
}

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

应用场景

优缺点

优点

缺点

Android中的源码分析

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

1、PackageParserparseBaseApkCommon方法。基于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)) {
    // 其他代码略

    // 解释application标签
    if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
      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进行解释。

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这个方法中解释。

上一篇 下一篇

猜你喜欢

热点阅读