解析器模式 (学习笔记2021.09.23)

解析器模式 (学习笔记2021.09.23)

前言:

解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法。

解释器模式,其实就是用来实现根据语法规则解读“句子”的解释器 。

实际上,理解这个概念,我们可以类比中英文翻译。我们知道,把英文翻译成中文是有一定规则的。这个规则就是定义中的“语法”。我们开发一个类似 Google Translate 这样的翻译器,这个翻译器能够根据语法规则,将输入的中文翻译成英文。这里的翻译器就是解释器模式定义中的“解释器

前提条件:

如何实现一个自定义接口告警规则功能?

在我们平时的项目开发中,监控系统非常重要,它可以时刻监控业务系统的运行情况,及时将异常报告给开发者。比如,如果每分钟接口出错数超过 100,监控系统就通过短信、微信、邮件等方式发送告警给开发者。

一般来讲,监控系统支持开发者自定义告警规则,比如我们可以用下面这样一个表达式,来表示一个告警规则,它表达的意思是:每分钟 API 总出错数超过 100 或者每分钟 API 总调用数超过 10000 就触发告警。

api_error_per_minute > 100 || api_count_per_minute > 10000

在监控系统中,告警模块只负责根据统计数据和告警规则,判断是否触发告警。至于每分钟 API 接口出错数、每分钟接口调用数等统计数据的计算,是由其他模块来负责的。其他模块将统计数据放到一个 Map 中(数据的格式如下所示),发送给告警模块。接下来,我们只关注告警模块。

Map<String, Long> apiStat = new HashMap<>();
apiStat.put("api_error_per_minute", 103);
apiStat.put("api_count_per_minute", 987);

为了简化讲解和代码实现,我们假设自定义的告警规则只包含“||、&&、>、<、==”这五个运算符,其中,“>、<、==”运算符的优先级高于“||、&&”运算符,“&&”运算符优先级高于“||”。在表达式中,任意元素之间需要通过空格来分隔。除此之外,用户可以自定义要监控的 key,比如前面的 api_error_per_minute、api_count_per_minute。

我们可以把自定义的告警规则,看作一种特殊“语言”的语法规则。我们实现一个解释器,能够根据规则,针对用户输入的数据,判断是否触发告警。利用解释器模式,我们把解析表达式的逻辑拆分到各个小类中,避免大而复杂的大类的出现。

使用解析器解决前提条件

1.0 创建表达式接口

/**
 * 表达式接口
 */
public interface Expression {

    /**
     * 执行解释( 获取是否符合表达式结果 ) stats= 等于其他组件传递过来的统计数据
     */
    boolean interpret(Map<String, Long> stats);
}

2.0 创建具体的几个表达式实现行为

/**
 * 第一种解析表达式规则,
 * 当统计值大于, 表达式定义的值时候, 可以发送警告
 */
public class GreaterExpression implements Expression {
    private String key;
    private long value;
    private boolean reverse;

    public GreaterExpression(String strExpression) {
        String[] elements = strExpression.trim().split("\\s+");
        if (elements.length != 3 || !elements[1].trim().equals(">")) {
            throw new RuntimeException("Expression is invalid: " + strExpression);
        }
        this.key = elements[0].trim();
        this.value = Long.parseLong(elements[2].trim());
    }

    @Override
    public boolean interpret(Map<String, Long> stats) {
       // 不包含此表达式规则key, 则跳过
        if (!stats.containsKey(key)) {
            return false;
        }
        // 取出统计数值
        long statValue = stats.get(key);
        // 当统计值大于, 表达式定义的值时候, 可以发送警告
        boolean result = statValue < value;
        if (reverse){
            result =  !result;
        }
        return result;
    }
}
// ------------------------------------LessExpression--------------------------------
/**
 * 第一种解析小于表达式规则,
 * 当统计值小于, 表达式定义的值时候, 可以发送警告
 */
public class LessExpression implements Expression {
    private String key;
    private long value;
    private boolean reverse;
    
    public LessExpression(String strExpression) {
        String[] elements = strExpression.trim().split("\\s+");
        if (elements.length != 3 || !elements[1].trim().equals("<")) {
            throw new RuntimeException("Expression is invalid: " + strExpression);
        }
        this.key = elements[0].trim();
        this.value = Long.parseLong(elements[2].trim());
    }

    @Override
    public boolean interpret(Map<String, Long> stats) {
       // 不包含此表达式规则key, 则跳过
        if (!stats.containsKey(key)) {
            return false;
        }
        // 取出统计数值
        long statValue = stats.get(key);
        // 当统计值小于, 表达式定义的值时候, 可以发送警告
        boolean result = statValue < value;
        if (reverse){
            result =  !result;
        }
        return result;
    }
}
// ---------------------------------------AndExpression-----------------------------------------
/**
 * 第三种解析并且表达式规则,
 * 当统计值要同时满足2个条件的表达式定义的值时候, 可以发送警告
 */
public class AndExpression implements Expression {

    private List<Expression> expressionList = new ArrayList<>(2);

    public AndExpression(String expression) {
        if (!StrUtil.isBlankOrUndefined(expression)) {
            String[] split = expression.trim().split("&&");
            for (String str : split) {
                // 包含小于条件
                if (str.contains("<")) {
                    expressionList.add(new LessExpression(str));
                    // 大于
                } else if (str.contains(">")) {
                    expressionList.add(new GreaterExpression(str));
                } else {
                    throw new RuntimeException("条件表达式不符合定义规则");
                }
            }
        }
    }

    @Override
    public boolean interpret(Map<String, Long> stats) {
        // 遍历所有表达式
        for (Expression expression : expressionList) {
            // 其中一个不满足, 说明解析器表达式不在满足
            if (!expression.interpret(stats)) {
                return false;
            }
        }
        // 最终如果2个都满足, 则是true
        return true;
    }
}

3.0 使用解析器进行测试

public void applicationTest() throws Exception {
        Map<String, Long> apiStat = new HashMap<>();
        apiStat.put("api_error_per_minute", 103L);
        apiStat.put("api_count_per_minute", 9L);
        // 解析并且条件表达式
        String expression = "api_error_per_minute > 10 && api_count_per_minute < 10";
        Expression andExpression = new AndExpression(expression);
        // 解析其他组件传递过来的统计数据
        if (andExpression.interpret(apiStat)) {
            System.out.println(" 表达式解析后符合警告发送, 进行发送!");
        }
    }
//---------------------------------------------
表达式解析后符合警告发送, 进行发送!
//----------------------------------------------    
    
public void applicationTest() throws Exception {
        Map<String, Long> apiStat = new HashMap<>();
        apiStat.put("api_error_per_minute", 103L);
        apiStat.put("api_count_per_minute", 99L);
        // 解析并且条件表达式
        String expression2 = "api_error_per_minute > 100";
        Expression greaterExpression = new GreaterExpression(expression2);
        // 解析其他组件传递过来的统计数据
        if (greaterExpression.interpret(apiStat)) {
            System.out.println(" 表达式解析后符合警告发送, 进行发送!");
        }
    }
//表达式解析后符合警告发送, 进行发送!

OrExpression 或者的表达式规则解析, 叫不在本文实现了

总结

解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法。实际上,这里的“语言”不仅仅指我们平时说的中、英、日、法等各种语言。从广义上来讲,只要是能承载信息的载体,我们都可以称之为“语言”,比如,古代的结绳记事、盲文、哑语、摩斯密码等。

要想了解“语言”要表达的信息,我们就必须定义相应的语法规则。这样,书写者就可以根据语法规则来书写“句子”(专业点的叫法应该是“表达式”),阅读者根据语法规则来阅读“句子”,这样才能做到信息的正确传递。而我们要讲的解释器模式,其实就是用来实现根据语法规则解读“句子”的解释器。

解释器模式的代码实现比较灵活,没有固定的模板。我们前面说过,应用设计模式主要是应对代码的复杂性,解释器模式也不例外。它的代码实现的核心思想,就是将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。一般的做法是,将语法规则拆分一些小的独立的单元,然后对每个单元进行解析,最终合并为对整个语法规则的解析。

4w3DfS.md.png
1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懵懵懂懂程序员

如果节省了你的时间, 请鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值