解释器模式意图为给定的语言定义其文法表示,同时定义该文法表示的一套解释器来解释语言中的句子。该模式说的简单通俗点,其主要用途是用来解释用的。至于解释什么,则要看具体的上下文环境。我们可以为一个表达式专门写一个解释器、也可以为一个语句写一个解释器,一个语句可以看成是由多个表达式构成的、因此,我们同样可以为一段文本写个解释器等等。而在要设计实现一个有效的解释器之前,试想下,首先要做的将是要为待解释的表达式进行其文法定义,即:定义其规则。一个先简单的例子,假如用户自己发明了一个脚本语言。如果想要让该语言行之有效,则必需为其定义各种“规则”,如:关键字是什么、表达式格式是什么、语句的构造又是有哪些约定的、如果有面向对象语法,则对象又是如何表示的、…等等,然后再需要实现一个能够识别这些“规则”并能正确解释、运行它们的编译器。
每个文法定义的一般来说是许许多多的表达式构成的。而复杂表达式又会由更细小的表达式构造成。比如:C++中的表达式:3 + 7) * 15;该表达式可被拆解为:表达式一:3 + 7);表达式二:15;以及表达式三:表达式一的结果 + 表达式二的结果。其中表达式二是最终不可再拆分的最小粒度表达式,称之为终结符表达式TerminalExpression。而最初整个表达式是可拆分的,它是由其他的子表达式构成的(其实如果该例子写的更复杂一些,则这个说明效果可能会更好),称之为非终结符表达式NoterminalExpression。有学过编译原理的人可能知道,编译器(其实就是一种解释器)的一般都是一种语法树的结构,而该语法树中的叶子其实就是这边的表达式二,非叶子是这边的表达式一或最初的整个表达式。对于一个四则运算的表达式的语法树,一般来说,叶子节点都是具体 的数字值,而连接这些数字值的节点一般是四则运算符,如:+-*/等。解释器模式的类结构图参考如下:
模式编码结构参考如下:
1 namespace interpreter 2 { 3 // 上下文 4 class Context {}; 5 6 // 抽象表达式 7 class IAbstractExpression 8 { 9 public: 10 // some code here........ 11 virtual void interpreteContext* pContext) {} 12 13 };//class IAbstractExpression 14 15 class TerminalExpression 16 { 17 public: 18 // some code here........ 19 virtual void interpreteContext* pContext) { /*some code here........*/ } 20 21 };//class TerminalExpression 22 23 class NoterminalExpression 24 { 25 public: 26 // some code here........ 27 virtual void interpreteContext* pContext) { /*some code here........*/ } 28 29 };//class NoterminalExpression 30 31 }//namespace interpreter
解释器模式编码结构参考
个人的理解,该模式还是挺复杂庞大的。虽说它的意图表述也就是简简单单地一个文法定义、并定义其解释器。而真要设计起来,其粒度还是会挺细的。就比如一个语言,是需要定义非常非常多的表达式的。一个Boolean就需要一个BooleanExpression、一个与运算也需要一个AndExpression表达式、一个变量需要一个变量表达式、一个加号需要一个表达式、…、最终还需要这些简单表达式的复合表达式。总之,为了能够正确解释上下文,需要做足一切准备,而且还需要前期分析的够透彻,否则,表达式的粒度不够细,则后期复用性不足;表达式的粒度太细,则设计起来,可能又会有冗余。另外,解释器模式,其实与复合模式也是非常像的。甚至可以看成是复合模式的一种特例。只是复合模式的意图是更通用的,表示一种“部分-整体”的关系。