• 浅谈设计模式-解释器模式


    书接上回,本篇讲一下行为型模式-解释器模式

    解释器模式

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

    大白话:设计一个程序,能解析拥有一定规则的表达式。比如正则表达式解析器。

    UML图

    Expression:定义语法解析器的接口,约定解析器的解释操作

    1. /**
    2. * 语法表达式
    3. */
    4. public interface Expression {
    5. /**
    6. * 语法解析
    7. */
    8. void interpret();
    9. }

    Interpret1Expression/Interpret2Expression:表达式解析器,解析表达式中特定语法,一个解析器对应一个语法规则。

    1. /**
    2. * 子表达式(解析1)
    3. * 负责完整表达式中一段解析
    4. */
    5. public class Interpret1Expression implements Expression{
    6. @Override
    7. public void interpret() {
    8. System.out.println("解析表达式中一段:子表达式1");
    9. }
    10. }
    1. /**
    2. * 子表达式(解析2)
    3. * 负责完整表达式中一段解析
    4. */
    5. public class Interpret2Expression implements Expression{
    6. @Override
    7. public void interpret() {
    8. System.out.println("解析表达式中一段:子表达式2");
    9. }
    10. }

    TerminalExpression:也是一个表达式解析器,一般表示终结,得出结果的语法,比如四则运算中,最后等于操作。

    1. /**
    2. * 子表达式(解析3)
    3. * 负责完整表达式中一段解析--表示解析终止
    4. */
    5. public class TerminalExpression implements Expression{
    6. @Override
    7. public void interpret() {
    8. System.out.println("解析表达式中一段:终止表达式");
    9. }
    10. }

    ExpressionInterpretParser:语法表达式解析器,这这个类中调用其他子解析器完整解析整个语法表达式式。担当总控的角色。

    1. /**
    2. * 表达式解析器
    3. */
    4. public class ExpressionInterpretParser {
    5. /**
    6. * 指定表达式解析
    7. * @param express
    8. */
    9. public void parse(String express){
    10. System.out.println("表达式:" + express);
    11. System.out.println("调用解析器:" );
    12. new Interpret1Expression().interpret();
    13. new Interpret2Expression().interpret();
    14. new TerminalExpression().interpret();
    15. }
    16. }

    App:客户端,上面逻辑调用者。

    1. public class App {
    2. public static void main(String[] args) {
    3. ExpressionInterpretParser interpret = new ExpressionInterpretParser();
    4. interpret.parse("这是一个表达式");
    5. }
    6. }

    结果:

    1. 表达式:这是一个表达式
    2. 调用解析器:
    3. 解析表达式中一段:子表达式1
    4. 解析表达式中一段:子表达式2
    5. 解析表达式中一段:终止表达式

    案例分析

    需求:设计一个解析器,能解析加减乘除运算

    最终表达式:1 2 3 4 5 + - * /

    预想结果: (((1+2)-3) *4) / 5 = 0

    Expression:定义解析解析语法规则

    SubExpression / MulExpression / DivExpression / AddExpression:分别是加减乘除语法解析规则实现

    NumExpression:数字语法解析规则实现

    ExpressionInterpretParser:规则解析器

    App:客户端

    具体解析规则类:

    AddExpression

    1. //加法
    2. public class AddExpression implements Expression{
    3. private Expression num1Expression; //第一个加数
    4. private Expression num2Expression; //第二个加数
    5. public AddExpression(Expression num1Expression, Expression num2Expression){
    6. this.num1Expression = num1Expression;
    7. this.num2Expression = num2Expression;
    8. }
    9. @Override
    10. public int interpret() {
    11. return num1Expression.interpret() + num2Expression.interpret();
    12. }
    13. @Override
    14. public String toString() {
    15. return "+";
    16. }
    17. }

    SubExpression

    1. //减法
    2. public class SubExpression implements Expression{
    3. private Expression num1Expression; //第一个减数
    4. private Expression num2Expression; //第二个减数
    5. public SubExpression(Expression num1Expression, Expression num2Expression){
    6. this.num1Expression = num1Expression;
    7. this.num2Expression = num2Expression;
    8. }
    9. @Override
    10. public int interpret() {
    11. return num1Expression.interpret() - num2Expression.interpret();
    12. }
    13. @Override
    14. public String toString() {
    15. return "-";
    16. }
    17. }

    MulExpression

    1. //乘法
    2. public class MulExpression implements Expression{
    3. private Expression num1Expression; //第一个乘数
    4. private Expression num2Expression; //第二个乘数
    5. public MulExpression(Expression num1Expression, Expression num2Expression){
    6. this.num1Expression = num1Expression;
    7. this.num2Expression = num2Expression;
    8. }
    9. @Override
    10. public int interpret() {
    11. return num1Expression.interpret() * num2Expression.interpret();
    12. }
    13. @Override
    14. public String toString() {
    15. return "*";
    16. }
    17. }

    DivExpression

    1. //乘法
    2. public class DivExpression implements Expression{
    3. private Expression num1Expression; //第一个除数
    4. private Expression num2Expression; //第二个除数
    5. public DivExpression(Expression num1Expression, Expression num2Expression){
    6. this.num1Expression = num1Expression;
    7. this.num2Expression = num2Expression;
    8. }
    9. @Override
    10. public int interpret() {
    11. return num1Expression.interpret() / num2Expression.interpret();
    12. }
    13. @Override
    14. public String toString() {
    15. return "/";
    16. }
    17. }

    ExpressionInterpretParser

    1. public class ExpressionInterpretParser {
    2. private LinkedList list = new LinkedList<>();
    3. private boolean isSymbol(String sy){
    4. return "+".equals(sy) || "-".equals(sy) || "*".equals(sy)|| "/".equals(sy);
    5. }
    6. private Integer operation(Expression num1, Expression num2, String symbol){
    7. if("+".equals(symbol)){
    8. return new AddExpression(num1,num2).interpret();
    9. }else if("-".equals(symbol)){
    10. return new SubExpression(num1,num2).interpret();
    11. }else if("*".equals(symbol)){
    12. return new MulExpression(num1,num2).interpret();
    13. }else if("/".equals(symbol)){
    14. return new DivExpression(num1,num2).interpret();
    15. }
    16. return null;
    17. }
    18. public void parse(String express){
    19. String[] items = express.split(" ");
    20. for (String item : items) {
    21. if(!this.isSymbol(item)){
    22. list.addLast(new NumExpression(item));
    23. System.out.println("入栈参数:" + item);
    24. }else{
    25. System.out.println("运算符:" + item);
    26. Expression num1 = list.removeFirst();
    27. Expression num2 = list.removeFirst();
    28. Integer ret = this.operation(num1, num2, item);
    29. if(ret == null){
    30. System.out.println("表达式解析异常,无法运算");
    31. return;
    32. }
    33. this.list.push(new NumExpression(ret));
    34. System.out.println("运算结果再入栈:" + ret);
    35. }
    36. }
    37. if(list.size() == 1){
    38. System.out.println("表达式运算结果:" + list.removeFirst());
    39. }
    40. }
    41. }

    App

    1. public class App {
    2. public static void main(String[] args) {
    3. new ExpressionInterpretParser().parse("1 2 3 4 5 + - * /");
    4. }
    5. }

    结果

    1. 入栈参数:1
    2. 入栈参数:2
    3. 入栈参数:3
    4. 入栈参数:4
    5. 入栈参数:5
    6. 运算符:+
    7. 运算结果再入栈:3
    8. 运算符:-
    9. 运算结果再入栈:0
    10. 运算符:*
    11. 运算结果再入栈:0
    12. 运算符:/
    13. 运算结果再入栈:0
    14. 表达式运算结果:0

    解析

    加减乘除规则解析类,实现+-*/四则运算逻辑,将计算出结果重新入队列,参与后续计算。

    适用场景

    一些规则引擎解析

    一些确定语法表达式需要解析时

    一些重复出现的问题可以用一种简单的语言来进行表达。

    优缺点

    优点

    语法由很多类表示,容易改变及扩展,新增表达式时,只需要添加新的表达式实现类即可,“开闭原则”。

    缺点

    当语法规则数目太多时,增加系统复杂度


    开发案例

    Spring自带了SpEl表达式

    1. public class SpringELTest {
    2. public static void main(String[] args) {
    3. SpelExpressionParser parser = new SpelExpressionParser();
    4. Expression expression = parser.parseExpression("(((1+2)-3) *4) / 5 ");
    5. System.out.println(expression.getValue());
    6. }
    7. }

    类体系:

    解析体系类:ExpressionParser

    1. public interface ExpressionParser {
    2. Expression parseExpression(String expressionString) throws ParseException;
    3. Expression parseExpression(String expressionString, ParserContext context) throws ParseException;
    4. }

    解析器实现类:SpelExpressionParser

    1. public class SpelExpressionParser extends TemplateAwareExpressionParser {
    2. @Override
    3. protected SpelExpression doParseExpression(String expressionString, @Nullable ParserContext context) throws ParseException {
    4. return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context);
    5. }
    6. }

    父类解析器

    1. public abstract class TemplateAwareExpressionParser implements ExpressionParser {
    2. private Expression parseTemplate(String expressionString, ParserContext context) throws ParseException {
    3. if (expressionString.isEmpty()) {
    4. return new LiteralExpression("");
    5. }
    6. Expression[] expressions = parseExpressions(expressionString, context);
    7. if (expressions.length == 1) {
    8. return expressions[0];
    9. }
    10. else {
    11. return new CompositeStringExpression(expressionString, expressions);
    12. }
    13. }
    14. }

    解析之后结果对象:Expression

    1. public interface Expression {
    2. //表达式规则
    3. String getExpressionString();
    4. //解析完后结果
    5. @Nullable
    6. Object getValue() throws EvaluationException;
    7. }

    总结

    解析器模式在特定领域,特定场景下才使用,相对其他模式来说,使用频率不高得设计模式啦。

    最后来总结一下:

    解析器模式的本质:分离实现,解释执行。

  • 相关阅读:
    Github提交和克隆代码步骤记录——图形界面/命令方式
    详解memcpy和memmove函数的使用
    WinEdt 11.1编辑器的尝鲜体验
    可爱猫+python3+Flask+aiohttp简单搭建微信机器人
    计算机操作系统第二章 进程的描述与控制
    在script标签中引用tiptap
    数据库基本操作(一)
    自己动手写编译器:创建由 C 语言编译而成的语法解析器
    项目运维工作的心得总结
    Unity3D Entity_CacheService实现详解
  • 原文地址:https://blog.csdn.net/langfeiyes/article/details/126358123