设计模式速记-解释器模式
解释器模式
解释器模式的定义如下:
给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。
解释器模式顾名思义即为处理一种具有特定文法语言的方法,比如“北京的小孩”、“天津的老人”、“河北的中年人”,这一组表达式都是同一种文法类型的,都是
主语(地名)+ 谓语(的)+ 宾语(小孩or老人or中年人)
发现这组语言的特点后,可以发现每句话分为三个部分,分别是地名
、的
、人群
,所以如果假设场景为做公交车,小孩和老人免费只有中年人需要交费的话,就需要对上述文法进行解释,这就需要使用解释器模式
优缺点
优点
- 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
- 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
缺点
- 行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
- 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
- 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
所以解释器模式的性能比较差,在实际使用中经常使用其他工具代替。
模式角色
- 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
- 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
- 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
- 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
- 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
实现例子
1. 场景描述
假设以北京-天津的火车为例,地点为北京和天津、人群为老人、小孩和年轻人。老人半价,小孩不需要买票。使用解释器模式
2. 代码示例
-
抽象表达式角色
/** * @Description 抽象表达式角色 */ public interface Expression { /** * 对 指令message 进行解释处理 * @param message 指令 * @return 指令处理结果 */ boolean interpret(String message); }
-
终结符表达式角色
/** * @Description 终结符表达式角色 */ public class TerminalExpression implements Expression { /** * 城市语言处理器 */ private Expression cityExpression; /** * 人群语言处理器 */ private Expression peopleExpression; public TerminalExpression(Expression cityExpression, Expression peopleExpression) { this.cityExpression = cityExpression; this.peopleExpression = peopleExpression; } @Override public boolean interpret(String message) { String[] wordArray = message.split("的"); if (wordArray.length != 2) { throw new RuntimeException("语法错误:" + message); } return cityExpression.interpret(wordArray[0]) && peopleExpression.interpret(wordArray[1]); } }
-
非终结符表达式角色
/** * @Description 非终结符表达式角色 */ public class NonTerminalExpression implements Expression { /** * 初始化内置数据 */ private Set<String> hashSet = Sets.newHashSet(); public NonTerminalExpression(List<String> dataList){ hashSet.addAll(dataList); } @Override public boolean interpret(String message) { return hashSet.contains(message); } }
-
环境角色
/** * @Description 环境角色,示例中指火车站 */ public class Station { private List<String> cityFree = Lists.newArrayList("北京", "天津"); private List<String> peopleFree = Lists.newArrayList("老人", "小孩"); private Expression cityPeopleExpression; public Station() { cityPeopleExpression = new TerminalExpression( new NonTerminalExpression(cityFree), new NonTerminalExpression(peopleFree) ); } /** * 检查资格 * * @param cityPeople “地名”的“人群” */ public void check(String cityPeople) { if (cityPeopleExpression.interpret(cityPeople)) { System.out.println(cityPeople + "可以免费乘车"); return; } System.out.println(cityPeople + "需要付费乘车"); } }
-
测试代码
public static void main(String[] args) { List<String> testString = Lists.newArrayList( "北京的小孩", "上海的小孩", "天津的年轻人", "北京的年轻人", "天津的老人", "北京的老人" ); Station station = new Station(); testString.forEach(station::check); }
-
测试结果
北京的小孩可以免费乘车 上海的小孩需要付费乘车 天津的年轻人需要付费乘车 北京的年轻人需要付费乘车 天津的老人可以免费乘车 北京的老人可以免费乘车