解释器模式

本文深入探讨了解释器模式,一种类行为型设计模式,用于处理语言文法的解析与执行。文章阐述了模式的优点,如良好的扩展性和实现的简易性,同时也指出了执行效率低和可能引起类膨胀的缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义

解释器(Interpreter)模式给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。

解释器模式属于类行为型模式。


要点

优点:

  1. 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
  2. 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。

缺点:

  1. 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
  2. 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
  3. 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。

主要角色:
抽象表达式(Abstract Expression):定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
终结符表达式(Terminal Expression):是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
非终结符表达式(Nonterminal Expression):也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
环境(Context):通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。

在这里插入图片描述


场景

解释器模式实现的关键是定义文法规则、设计终结符类与非终结符类、画出结构图,必要时构建语法树。

假如“京津冀”公交车读卡器可以判断乘客的身份,如果是【北京】或者【天津】的 “老人” 、 “儿童” 就可以免费乘车,其他人员乘车一次扣 1 元。

实现
/**
 * 抽象表达式类
 */
interface Expression {

    boolean interpret(String info);

}

/**
 * 终结符表达式类
 */
class TerminalExpression implements Expression {

    private Set<String> set = new HashSet<>();

    public TerminalExpression(String[] data) {
        Collections.addAll(set, data);
    }

    @Override
    public boolean interpret(String info) {
        return set.contains(info);
    }

}

/**
 * 非终结符表达式类
 */
class AndExpression implements Expression {

    private Expression city;
    private Expression person;

    public AndExpression(Expression city, Expression person) {
        this.city = city;
        this.person = person;
    }

    @Override
    public boolean interpret(String info) {
        String[] s = info.split("的");
        return city.interpret(s[0]) && person.interpret(s[1]);
    }

}


/**
 * 环境类
 */
class Context {

    private String[] citys = {"北京", "天津"};
    private String[] persons = {"老人", "儿童"};
    private Expression cityPerson;

    public Context() {
        Expression city = new TerminalExpression(citys);
        Expression person = new TerminalExpression(persons);
        cityPerson = new AndExpression(city, person);
    }

    public void freeRide(String info) {
        boolean ok = cityPerson.interpret(info);
        if (ok) {
            System.out.println("您是" + info + ",您本次乘车免费!");
        } else {
            System.out.println(info + ",您不是免费人员,本次乘车扣费1元!");
        }
    }
}
public class Client {

    /**
     * 文法规则
     * <expression> ::= <city>的<person>
     * <city> ::= 北京 | 天津
     * <person> ::= 老人 | 儿童
     */
    public static void main(String[] args) {
        Context bus = new Context();

        bus.freeRide("北京的老人");
        bus.freeRide("北京的中年人");
        bus.freeRide("天津的妇女");
        bus.freeRide("天津的儿童");
        bus.freeRide("河北的中年人");
    }

}

----------------输出----------------
您是北京的老人,您本次乘车免费!
北京的年轻人,您不是免费人员,本次乘车扣费1元!
天津的妇女,您不是免费人员,本次乘车扣费1元!
您是天津的儿童,您本次乘车免费!
河北的年轻人,您不是免费人员,本次乘车扣费1元!

源码

Click here

总结

适用场景:

  1. 当语言的文法较为简单,且执行效率不是关键问题时。
  2. 当问题重复出现,且可以用一种简单的语言来进行表达时。
  3. 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。

解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。如果碰到对表达式的解释,在 Java 中可以用 Expression4J 或 Jep 等来设计。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值