Skip to content

领域规则模式

概念

定义

  • 在特定领域中,某些变化虽然频繁,但可以抽象为某种规则。
  • 这时候结合特定领域,将问题抽象为语法规则,从而给出在该领域下的一般性解决方案。

解释器模式(Interpreter)

动机

  • 在软件构建过程中,如果某一特定领域的问题比较复杂,类似的结构不断重复出现,如果使用普通的编程方式来实现将面临非常频繁的变化。
  • 在这种情况下, 「将特定领域问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子」,从而达到解决问题的目的。

模式定义

  • 给定一个语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。
  • 即,用编译语言的方式来分析应用中的实例。
  • 编译原理中「文法、句子、语法树」概念

    • “文法”:语法规则。
    • “句子”:语言的基本单位,语言集中的一个元素,它由终结符构成。
    • “语法树”:句子结构的树型表示,句子的推导结果。

UML 类图

x

角色

  • 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
  • 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
  • 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
  • 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
  • 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。

代码一

/**
 * 文法规则
 * <expression> ::= <city>的<person>
 * <city> ::= 韶关|广州
 * <person> ::= 老人|妇女|儿童
 **/

// 抽象表达式类
interface Expression {
  interpret(info: string): boolean;
}

// 终结符表达式类(叶子结点)
class TerminalExpression implements Expression {
  private set: Set<string> = new Set<string>();

  public constructor(data: string[]) {
    for (let i = 0; i < data.length; i++) {
      this.set.add(data[i]);
    }
  }

  public interpret(info: string): boolean {
    if (this.set.has(info)) {
      return true;
    }
    return false;
  }
}

// 非终结符表达式类(非叶子结点)
class AndExpression implements Expression {
  private city: Expression = null;
  private person: Expression = null;

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

  public interpret(info: string): boolean {
    const s: string[] = info.split('的');
    return this.city.interpret(s[0]) && this.person.interpret(s[1]);
  }
}

// 环境类
class Context {
  private cities: string[] = ['韶关', '广州'];
  private persons: string[] = ['老人', '妇女', '儿童'];
  private cityPerson: Expression;

  public constructor() {
    const city = new TerminalExpression(this.cities);
    const person = new TerminalExpression(this.persons);
    this.cityPerson = new AndExpression(city, person);
  }

  public freeRide(info: string): void {
    const ok: boolean = this.cityPerson.interpret(info);

    if (ok) {
      console.log('您是' + info + ',您本次乘车免费!');
    } else {
      console.log(info + ',您不是免费人员,本次乘车扣费2元!');
    }
  }
}

export default class InterpreterPatternDemo {
  public static main(args: string[]): void {
    const bus: Context = new Context();

    bus.freeRide('韶关的老人');
    bus.freeRide('韶关的年轻人');
    bus.freeRide('广州的妇女');
    bus.freeRide('广州的儿童');
    bus.freeRide('山东的儿童');
  }
}

代码二

/**
 * 文法规则
 * <expression> ::= a+b-c+d
 * <var> ::= a,b,c,d
 * <symbol> ::= +/-
 **/

// 抽象表达式类
interface Expression<K, V> {
  interpret(item: Map<K, V>): number;
}

// 终结符表达式类(叶子结点)
class VarExpression implements Expression<string, number> {
  private key: string;

  constructor(key: string) {
    this.key = key;
  }

  public interpret(item: Map<string, number>): number {
    return item.get(this.key);
  }
}

// 非终结符表达式类(非叶子结点)
abstract class SymbolExpression implements Expression<string, number> {
  protected left: Expression<string, number>;
  protected right: Expression<string, number>;

  constructor(
    left: Expression<string, number>,
    right: Expression<string, number>
  ) {
    this.left = left;
    this.right = right;
  }

  abstract interpret(item: Map<string, number>): number;
}

// 加法运算
class AddExpression extends SymbolExpression {
  public interpret(item: Map<string, number>): number {
    return this.left.interpret(item) + this.right.interpret(item);
  }
}

// 减法运算
class SubExpression extends SymbolExpression {
  public interpret(item: Map<string, number>): number {
    return this.left.interpret(item) - this.right.interpret(item);
  }
}

// 环境类
class Context {
  private stack = [];
  private left: Expression<string, number>;
  private right: Expression<string, number>;

  public analyse(expr: string): Expression<string, number> {
    for (let i = 0; i < expr.length; i++) {
      switch (expr[i]) {
        case '+':
          // 加法运算
          this.left = this.stack.pop();
          this.right = new VarExpression(expr[++i]);
          this.stack.push(new AddExpression(this.left, this.right));
          break;
        case '-':
          // 减法运算
          this.left = this.stack.pop();
          this.right = new VarExpression(expr[++i]);
          this.stack.push(new SubExpression(this.left, this.right));
          break;
        default:
          // 变量表达式
          this.stack.push(new VarExpression(expr[i]));
          break;
      }
    }

    const expression: Expression<string, number> = this.stack.pop();
    return expression;
  }
}

export default class InterpreterPatternDemo {
  public static main(args: string[]): void {
    const str = 'a+b-c+d-e';
    const map = new Map();
    map.set('a', 1);
    map.set('b', 2);
    map.set('c', 3);
    map.set('d', 4);
    map.set('e', 5);

    const ctx: Context = new Context();
    const expression = ctx.analyse(str);
    const result = expression.interpret(map);
    console.log(result);
  }
}

要点总结

  • Interpreter模式的应用场合是Interpreter模式应用中的难点,只有满足“业务规则频繁变化,且类似的结构不断重复出现,并且容易抽象为语法规则的问题”才适合使用Interpreter模式。
  • 使用Interpreter模式来表示文法规则,从而可以使用面向对象技巧来方便地“扩展”文法。
  • Interpreter模式比较适合简单的文法表示,对于复杂的文法表示,Interpreter模式会产生比较大的类层次结构,需要求助于语法分析生成器这样的标准工具。