解释器模式--相亲的公式

2022-05-16 14:04:43 浏览数 (1)

引子

小美最近被家里人催着相亲,家里亲戚介绍了好几个男生,小美都不满意。

小美的要求高着呢:起码要有房吧?年收入不能低于20万吧?身高最低也要175cm吧?年龄也不能太大,不能超过30岁吧?学历么也要本科起步吧?

什么?你觉得我要求太高了?好说,如果你家里很有钱的话,其他条件也可以放宽嘛!

小美不愧是程序媛出身,她把自己的相亲条件列成了一条公式:

房子 >= 1 && 年收入 >= 20 && 身高 >= 175 && 年龄 < 30 && 学历 >= 4 || 财产 >= 1000

小美把这条公式交给了媒婆,并嘱咐一定要满足这条公式的男生才可以安排相亲哦。

媒婆一看直接懵逼,怎么还有公式呀,姑娘,我只是个牵线的,又没学过数学,你这不是为难我吗?

小美说道:您不要急,套用了公式才能保证结果正确嘛,您听我细细道来,这个还得从解释器模式开始讲起...

媒婆:这我听不懂啊...

小美:不,您听得懂...

解释器模式

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

(Interpreter pattern is used to defines a grammatical representation for a language and provides an interpreter to deal with this grammar.)

用大白话说就是:我们可以自己定义一些符号,然后我们给这些符号设定规则,解释权在我们自己手里。

比如一个表达式”1#2!3“,我们可以定义”#“表示” “号,”!”表示“-”号,怎么解释我们自己说了算。

解释器模式的类图如下:

我用表达式a b-c来套用一下:

  • AbstractExpression:抽象解释器,具体的解释任务由各个实现类完成,具体的解释器分别由TerminalExpression和NonterminalExpression完成。
  • TerminalExpression:终结符表达式,就是对应“a”,“b”,“c”。
  • NonterminalExpression:非终结符表达式,就是对应“ ”,“-”符号。
  • Context:解释器之外的一些全局信息,在下面的例子中就是客户端中的Map。

针对相亲的表达式,采用了下图的结构:

说完,小美就抛出了一段代码:

这里我们假设“ >= ”,“<”符号的优先级比“&&”和“||”高,“&&”的优先级比“||”高。

表达式接口类:

代码语言:javascript复制
/**
 * 表达式接口
 */
public interface Expression {
    /**
     * 解释表达式
     * @param states
     * @return
     */
    boolean interpret(Map<String, Integer> states);
}

大于等于表达式类:

代码语言:javascript复制
/**
 * 大于等于表达式
 */
public class GreaterOrEqualExpression implements Expression {
    private String key;
    private int value;

    public GreaterOrEqualExpression(String key, int value) {
        this.key = key;
        this.value = value;
    }

    public GreaterOrEqualExpression(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 = Integer.parseInt(elements[2].trim());
    }

    /**
     * 解释大于等于表达式
     * @param states
     * @return
     */
    @Override
    public boolean interpret(Map<String, Integer> states) {
        if(!states.containsKey(key)) {
            return false;
        }
        int stateValue = states.get(key);
        return stateValue >= value;
    }
}

小于表达式类:

代码语言:javascript复制
/**
 * 小于表达式
 */
public class LessExpression implements Expression {
    private String key;
    private int value;

    public LessExpression(String key, int value) {
        this.key = key;
        this.value = value;
    }

    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 = Integer.parseInt(elements[2].trim());
    }

    /**
     * 解释小于表达式
     * @param states
     * @return
     */
    @Override
    public boolean interpret(Map<String, Integer> states) {
        if(!states.containsKey(key)) {
            return false;
        }
        int stateValue = states.get(key);
        return stateValue < value;
    }
}

与表达式类:

代码语言:javascript复制
/**
 * 与表达式
 */
public class AndExpression implements Expression {
    private List<Expression> expressions = new ArrayList<>();

    public AndExpression(List<Expression> expressions) {
        this.expressions = expressions;
    }

    public AndExpression(String strAndExpression) {
        String[] strExpressions = strAndExpression.split("&&");
        for(String strExpr : strExpressions) {
            if(strExpr.contains(">=")) {
                expressions.add(new GreaterOrEqualExpression(strExpr));
            } else if(strExpr.contains("<")) {
                expressions.add(new LessExpression(strExpr));
            } else {
                throw new RuntimeException("Expression is invalid: "   strAndExpression);
            }
        }
    }

    /**
     * 解释与表达式
     *
     * @param states
     * @return
     */
    @Override
    public boolean interpret(Map<String, Integer> states) {
        for(Expression expr : expressions) {
            if(!expr.interpret(states)) {
                return false;
            }
        }
        return true;
    }
}

或表达式类:

代码语言:javascript复制
/**
 * 或表达式
 */
public class OrExpression implements Expression {
    private List<Expression> expressions = new ArrayList<>();

    public OrExpression(List<Expression> expressions) {
        this.expressions = expressions;
    }

    public OrExpression(String strOrExpression) {
        String[] andExpressions = strOrExpression.split("\|\|");
        for(String andExpr : andExpressions) {
         // &&表达式的优先级比||表达式高,计算||表达式之前要先计算&&表达式
            // 这里复用&&表达式
            expressions.add(new AndExpression(andExpr));
        }
    }

    /**
     * 解释或表达式
     *
     * @param states
     * @return
     */
    @Override
    public boolean interpret(Map<String, Integer> states) {
        for(Expression expr : expressions) {
            if(expr.interpret(states)) {
                return true;
            }
        }
        return false;
    }
}

相亲表达式解析类:

代码语言:javascript复制
/**
 * 相亲表达式解析
 */
public class BlindDateRuleInterpreter {

    private Expression expression;

    /**
     * 先从或表达式开始解析
     * @param ruleExpression
     */
    public BlindDateRuleInterpreter(String ruleExpression) {
        this.expression = new OrExpression(ruleExpression);
    }

    public boolean interpret(Map<String, Integer> states) {
        return expression.interpret(states);
    }
}

客户端测试类:

代码语言:javascript复制
/**
 * 客户端测试类
 */
public class ClientTest {
    public static void main(String[] args) {
        // 为了更贴近生活,这里的key就用中文了
        String rule = "房子 >= 1 && 年收入 >= 20 && 身高 >= 175 && 年龄 < 30 && 学历 >= 4 || 财产 >= 1000";
        BlindDateRuleInterpreter interpreter = new BlindDateRuleInterpreter(rule);
        Map<String, Integer> states = new HashMap<>();
        states.put("房子", 1);
        states.put("年收入", 20);
        states.put("身高", 176);
        states.put("年龄", 28);
        states.put("学历", 4);
        states.put("财产", 100);
        // 根据表达式判断相亲条件
        boolean qualified = interpreter.interpret(states);
        if(qualified) {
            System.out.println("兄弟,恭喜你符合条件啦!");
        } else {
            System.out.println("兄弟,还需继续努力!");
        }
    }
}

输出:

代码语言:javascript复制
兄弟,恭喜你符合条件啦!

总结

解释器模式描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。

解释器的核心就是将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。一般的做法是,将语法规则拆分成一些小的独立的单元,然后对每个单元进行解析,最终合并为对整个语法规则的解析。

优点

  • 解释器是一个简单语法分析工具,它最显著的优点就是扩展性,修改语法规则只要修改相应的非终结符表达式就可以了,若扩展语法,则只要增加非终结符类就可以了。

缺点

  • 解释器模式会引起类膨胀,每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了很多麻烦。
  • 解释器模式可能会使用大量的循环和递归,效率是一个不容忽视的问题,特别是用于解析复杂、冗长的语法时,效率比较低。

后记

小美:阿姨您好,这都三个月过去了,您怎么一个男生也没给我介绍啊?

媒婆:哎呀,这不是没有合适的嘛!我把十几个男生的条件套进公式只有一个符合条件的,然后对方又觉得你的条件不够好啦。别急,我再给你找找。

小美:额...那我不要公式了,只要人品好、价值观正就行,品性好最重要,其他的见了面再说吧。

媒婆:那好,这就好办了嘛,我给你留意留意。

0 人点赞