糖糖的vlog:不只分享生活,更点亮你心中的小确幸
文章目录
个人感悟
概念
适配场景
1 适合的场景
2 常见场景举例
实现方法
1 概念理解
3.
1 文法
3.
2 终结符和非终结符
3.
3 句子
3.
4语法树
2 实现思路
3 UML类图
4 代码示例
优缺点
1 优点
2 缺点
个人感悟解释器模式旨在定义语法规则并对符合语法规则的句子进行解释解释器模式使用场景专业且有限实际工作中场景很少可以帮助简单理解编译器原理模式涉及很多编译原理的概念看了一些文章还是一直半解这里把自己理解的内容记录下来先不纠结了关于四则运行的示例找了一个比较泛用、易懂的代码有兴趣可以看看核心在使用stack构建解释器对于基础的数据表达式分析计算已有很多成熟的开源底代码大家实际工作中遇到可以了解下比如Expression4J、MESP(Math Expression String Parser) 、Jep等
概念英文定义(《设计模式可复用面向对象软件的基础》)Given a language, define a represention for its grammar along with an interpreter that uses the representation to interpret sentences in the language.中文翻译给定一个语言定义它的文法的一种表示并定义一个解释器该解释器使用该表示来解释语言中的句子。
理解语言导向的设计模式专门为解释和执行特定领域语言而设计文法与解释分离将文法规则抽象为类结构解释逻辑分布在各个表达式类中组合模式的应用通常使用组合模式构建抽象语法树来表示语言结构关注点分离文法定义、语法解析、解释执行各司其职
适配场景
1 适合的场景简单文法规则当语言的文法相对简单规则数量有限时执行效率非关键对解释执行效率要求不高的场景频繁修改文法当文法需要经常扩展或修改时领域特定语言(DSL)需要为特定领域创建专用解释器避免重复解析需要多次执行相同或类似表达式
2 常见场景举例正则表达式引擎解释并执行正则表达式匹配规则SQL查询解释器解释简单的SQL查询语句业务规则引擎解释执行业务规则如优惠券规则、风控规则数学公式计算器解释数学表达式并计算结果配置文件解析解释特定格式的配置文件机器人指令系统解释机器人控制指令序列简单脚本语言实现简单的脚本语言解释器
实现方法
1 概念理解定义里提到了很多编译原理中的概念首先了解下各个概念含义有利于理解模式以逆波兰表示法(项在前 运算符在后)的四则运算的四则运算为例
3.
1 文法定义形式语言的规则系统定义了语言中合法句子的结构规则。
通常使用产生式规则描述。
理解*文法是定义语法规则的比如我们的汉语、正则表达式、sql、甚至代码都有语法符合规则才能正确解析。
所以解释器一般都有很强的专业性。
对于文法的表示通常用BNF巴科斯-诺尔范式),有兴趣可以了解下例子:四则运行expression :: term term operator | expression term operator | term term :: number | variable | expression number :: [
] variable :: [a-zA-Z_][a-zA-Z
_]* operator :: | - | * | /规则解释规则1: 表达式 → 项 项 运算符 规则2: 表达式 → 表达式 项 运算符 规则3: 项 → 数字 | 变量 | (表达式) 规则4: 数字 → 一个或多个数字字符 规则5: 变量 → 字母或下划线开头后跟字母、数字或下划线 规则6: 运算符 → | - | * | /
3.
2 终结符和非终结符定义终结符文法中的基本符号不能再被推导对应语言的最小单位如数字、变量、运算符。
非终结符可以由终结符和/或其他非终结符组成表示语言的结构单元如表达式、项、因子。
理解*看完语法树的概念再回过头看这个会好理解点
3.
3 句子定义符合文法规则的完整输入字符串是解释器要处理的具体实例理解*规则的实例。
我们经常说句子通不通顺实际上是汉语实例是否符合汉语语法。
例子表达式1: 3 4 // 符合项 项 运算符 表达式2: 3 4 5 * // 符合表达式 项 运算符 表达式3: 3 4 5 * // 符合表达式 项 运算符 表达式4: 3 4 // 无效运算符在前 表达式5: 3 4 // 无效缺少运算符 表达式6: (3
// 无效有括号
3.
4语法树定义句子结构的树形表示根节点是起始符号内部节点是非终结符叶子节点是终结符反映了句子的层次结构。
例子34 * 5* / \ 5 / \ 3
4
2 实现思路定义文法规则分析要解释的语言用形式文法描述其结构设计抽象语法树节点为每个文法规则创建对应的表达式类实现终结符表达式创建表示基本元素的类如数字、变量实现非终结符表达式创建表示组合结构的类如运算表达式定义上下文环境创建包含解释器所需全局信息的上下文类构建解释器接口定义统一的解释方法接口实现解释逻辑在每个表达式类中实现具体的解释操作添加构建器或解析器可选用于将输入字符串转换为抽象语法树
3 UML类图![[解释器模式_UML.png]]角色说明AbstractExpression抽象表达式声明解释操作的接口TerminalExpression终结符表达式实现与文法终结符相关的解释操作NonterminalExpression非终结符表达式实现与文法非终结符相关的解释操作Context上下文包含解释器之外的全局信息Client构建抽象语法树并调用解释操作
4 代码示例背景逆波兰表示法(项在前 运算符在后)的四则运算的四则运算核心是stack构建解释器逻辑表达式接口publicinterfaceExpression{intinterpret(Contextcontext);}上下文publicclassContext{privateMapString,IntegervariablesnewHashMap();publicvoidassign(Stringvariable,intvalue){variables.put(variable,value);}publicintgetValue(Stringvariable){Integervaluevariables.get(variable);if(valuenull){thrownewIllegalArgumentException(Variable not found: variable);}returnvalue;}}终结符表达式// 变量publicclassVariableExpressionimplementsExpression{privateStringname;publicVariableExpression(Stringname){this.namename;}Overridepublicintinterpret(Contextcontext){returncontext.getValue(name);}}// 数字publicclassNumberExpressionimplementsExpression{privateintnumber;publicNumberExpression(intnumber){this.numbernumber;}Overridepublicintinterpret(Contextcontext){returnnumber;}}非终结符表达式// 加publicclassAddExpressionimplementsExpression{privateExpressionleft;privateExpressionright;publicAddExpression(Expressionleft,Expressionright){this.leftleft;this.rightright;}Overridepublicintinterpret(Contextcontext){returnleft.interpret(context)right.interpret(context);}}// 减publicclassSubtractExpressionimplementsExpression{privateExpressionleft;privateExpressionright;publicSubtractExpression(Expressionleft,Expressionright){this.leftleft;this.rightright;}Overridepublicintinterpret(Contextcontext){returnleft.interpret(context)-right.interpret(context);}}// 乘publicclassMultiplyExpressionimplementsExpression{privateExpressionleft;privateExpressionright;publicMultiplyExpression(Expressionleft,Expressionright){this.leftleft;this.rightright;}Overridepublicintinterpret(Contextcontext){returnleft.interpret(context)*right.interpret(context);}}// 除publicclassDivideExpressionimplementsExpression{privateExpressionleft;privateExpressionright;publicDivideExpression(Expressionleft,Expressionright){this.leftleft;this.rightright;}Overridepublicintinterpret(Contextcontext){intdivisorright.interpret(context);if(divisor
{thrownewArithmeticException(Division by zero);}returnleft.interpret(context)/divisor;}}表达式构建构器publicclassExpressionBuilder{publicstaticExpressionbuild(Stringexpression){StackExpressionstacknewStack();String[]tokensexpression.split(\\s);for(Stringtoken:tokens){switch(token){case:Expressionrightstack.pop();Expressionleftstack.pop();stack.push(newAddExpression(left,right));break;case-:rightstack.pop();leftstack.pop();stack.push(newSubtractExpression(left,right));break;case*:rightstack.pop();leftstack.pop();stack.push(newMultiplyExpression(left,right));break;case/:rightstack.pop();leftstack.pop();stack.push(newDivideExpression(left,right));break;default:// 尝试解析为数字否则视为变量try{intnumberInteger.parseInt(token);stack.push(newNumberExpression(number));}catch(NumberFormatExceptione){stack.push(newVariableExpression(token));}break;}}returnstack.pop();}}测试publicclassClient{staticvoidmain(){// 创建上下文ContextcontextnewContext();context.assign(a,
;context.assign(b,
;context.assign(c,
;Expressionexpression1ExpressionBuilder.build(a b c * );intresult1expression
interpret(context);System.out.println(a b * c result
;// 输出: a b * c 20}}结果a b * c
优缺点
1 优点高内聚每个表达式类只关注自己的解释逻辑职责单一易于扩展语法添加新的表达式类即可扩展语言功能符合开闭原则可复用性高表达式类可以在不同的解释器中复用可维护性好文法规则与解释逻辑分离修改文法不影响解释器结构灵活性可以通过组合不同的表达式创建复杂的语言结构良好的可读性语法树直观反映了语言结构便于理解和调试
2 缺点效率较低复杂语法树需要递归解释性能可能不佳类数量爆炸文法复杂时需要创建大量的表达式类可维护性挑战对于复杂文法类层次结构会变得复杂难以维护不适合复杂文法文法过于复杂时解释器模式不是最佳选择稳定性受影响文法频繁变化时需要大量修改代码违反单一职责原则文法规则和解释逻辑混合在表达式类中参考:韩顺平 Java设计模式行无际 解释器模式(Interpreter Pattern)——自定义语言的实现kosamino 设计模式之解释器模式Interpreter详解及代码示例
9·1安装免费版-9·1安装免费版应用