解释器模式(Interpreter)
前言
解释器模式给定一个语言,定义它的文法的一种表示,并定义一个解释器使用这个表示来解释语言中的句子。它是 DSL(领域特定语言)、规则引擎、表达式求值的核心模式。本文将详细讲解解释器模式的核心原理及实际应用。
一、核心概念
1.1 定义
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
1.2 核心角色
| 角色 | 说明 |
|---|---|
| 抽象表达式(AbstractExpression) | 声明 interpret 方法 |
| 终结符表达式(TerminalExpression) | 解释最小单元(变量、常量) |
| 非终结符表达式(NonterminalExpression) | 组合表达式(与、或、加、减) |
| 上下文(Context) | 全局信息,变量存储 |
| 客户端(Client) | 构建语法树并解释 |
1.3 BNF 文法示例
表达式 ::= 加法 | 减法 | 变量 | 数字
加法 ::= 表达式 '+' 表达式
减法 ::= 表达式 '-' 表达式
变量 ::= [a-z]+
数字 ::= [0-9]+二、代码实现
2.1 基础实现:布尔表达式解释器
php
<?php
// 上下文:变量存储
class Context
{
private array $variables = [];
public function assign(string $name, bool $value): void
{
$this->variables[$name] = $value;
}
public function lookup(string $name): bool
{
if (!array_key_exists($name, $this->variables)) {
throw new RuntimeException("Variable {$name} not defined");
}
return $this->variables[$name];
}
}
// 抽象表达式
interface BooleanExpression
{
public function interpret(Context $context): bool;
}
// 终结符:变量
class VariableExpression implements BooleanExpression
{
public function __construct(private string $name) {}
public function interpret(Context $context): bool
{
return $context->lookup($this->name);
}
}
// 终结符:常量
class ConstantExpression implements BooleanExpression
{
public function __construct(private bool $value) {}
public function interpret(Context $context): bool
{
return $this->value;
}
}
// 非终结符:与操作
class AndExpression implements BooleanExpression
{
public function __construct(
private BooleanExpression $left,
private BooleanExpression $right
) {}
public function interpret(Context $context): bool
{
return $this->left->interpret($context) && $this->right->interpret($context);
}
}
// 非终结符:或操作
class OrExpression implements BooleanExpression
{
public function __construct(
private BooleanExpression $left,
private BooleanExpression $right
) {}
public function interpret(Context $context): bool
{
return $this->left->interpret($context) || $this->right->interpret($context);
}
}
// 非终结符:非操作
class NotExpression implements BooleanExpression
{
public function __construct(private BooleanExpression $expr) {}
public function interpret(Context $context): bool
{
return !$this->expr->interpret($context);
}
}
// 使用
// 表达式:(x AND y) OR (NOT z)
$expr = new OrExpression(
new AndExpression(new VariableExpression('x'), new VariableExpression('y')),
new NotExpression(new VariableExpression('z'))
);
$ctx = new Context();
$ctx->assign('x', true);
$ctx->assign('y', false);
$ctx->assign('z', false);
echo 'Result: ' . ($expr->interpret($ctx) ? 'true' : 'false') . PHP_EOL;
// (T AND F) OR (NOT F) = F OR T = true2.2 实际案例:算术表达式求值
php
<?php
// 上下文:变量存储
class MathContext
{
private array $variables = [];
public function setVariable(string $name, float $value): void
{
$this->variables[$name] = $value;
}
public function getVariable(string $name): float
{
if (!array_key_exists($name, $this->variables)) {
throw new RuntimeException("Unknown: {$name}");
}
return $this->variables[$name];
}
}
// 抽象表达式
interface Expression
{
public function interpret(MathContext $context): float;
}
// 终结符:数字
class NumberExpression implements Expression
{
public function __construct(private float $value) {}
public function interpret(MathContext $context): float
{
return $this->value;
}
}
// 终结符:变量
class VariableExpression implements Expression
{
public function __construct(private string $name) {}
public function interpret(MathContext $context): float
{
return $context->getVariable($this->name);
}
}
// 非终结符:加法
class AddExpression implements Expression
{
public function __construct(
private Expression $left,
private Expression $right
) {}
public function interpret(MathContext $context): float
{
return $this->left->interpret($context) + $this->right->interpret($context);
}
}
// 非终结符:减法
class SubtractExpression implements Expression
{
public function __construct(
private Expression $left,
private Expression $right
) {}
public function interpret(MathContext $context): float
{
return $this->left->interpret($context) - $this->right->interpret($context);
}
}
// 非终结符:乘法
class MultiplyExpression implements Expression
{
public function __construct(
private Expression $left,
private Expression $right
) {}
public function interpret(MathContext $context): float
{
return $this->left->interpret($context) * $this->right->interpret($context);
}
}
// 非终结符:除法
class DivideExpression implements Expression
{
public function __construct(
private Expression $left,
private Expression $right
) {}
public function interpret(MathContext $context): float
{
$divisor = $this->right->interpret($context);
if ($divisor == 0) {
throw new RuntimeException('Division by zero');
}
return $this->left->interpret($context) / $divisor;
}
}
// 使用:构建语法树 (x + 5) * (y - 2)
$expression = new MultiplyExpression(
new AddExpression(new VariableExpression('x'), new NumberExpression(5)),
new SubtractExpression(new VariableExpression('y'), new NumberExpression(2))
);
$ctx = new MathContext();
$ctx->setVariable('x', 10);
$ctx->setVariable('y', 8);
echo 'Result: ' . $expression->interpret($ctx) . PHP_EOL; // (10+5) * (8-2) = 902.3 实际案例:规则引擎
php
<?php
// 上下文:用户信息
class UserContext
{
public function __construct(
public int $age,
public int $vipLevel,
public float $purchaseAmount,
public int $registeredDays
) {}
}
// 抽象规则
interface Rule
{
public function interpret(UserContext $ctx): bool;
}
// 终结符:年龄判断
class AgeRule implements Rule
{
public function __construct(
private int $min,
private int $max
) {}
public function interpret(UserContext $ctx): bool
{
return $ctx->age >= $this->min && $ctx->age <= $this->max;
}
}
// 终结符:VIP 等级判断
class VipLevelRule implements Rule
{
public function __construct(private int $minLevel) {}
public function interpret(UserContext $ctx): bool
{
return $ctx->vipLevel >= $this->minLevel;
}
}
// 终结符:消费金额判断
class PurchaseAmountRule implements Rule
{
public function __construct(private float $minAmount) {}
public function interpret(UserContext $ctx): bool
{
return $ctx->purchaseAmount >= $this->minAmount;
}
}
// 非终结符:AND
class AndRule implements Rule
{
/** @param Rule[] $rules */
public function __construct(private array $rules) {}
public function interpret(UserContext $ctx): bool
{
foreach ($this->rules as $rule) {
if (!$rule->interpret($ctx)) {
return false;
}
}
return true;
}
}
// 非终结符:OR
class OrRule implements Rule
{
/** @param Rule[] $rules */
public function __construct(private array $rules) {}
public function interpret(UserContext $ctx): bool
{
foreach ($this->rules as $rule) {
if ($rule->interpret($ctx)) {
return true;
}
}
return false;
}
}
// 非终结符:NOT
class NotRule implements Rule
{
public function __construct(private Rule $rule) {}
public function interpret(UserContext $ctx): bool
{
return !$this->rule->interpret($ctx);
}
}
// 使用:构建规则
// 规则:(年龄 18-65 AND VIP>=3) OR (消费 >= 10000)
$rule = new OrRule([
new AndRule([new AgeRule(18, 65), new VipLevelRule(3)]),
new PurchaseAmountRule(10000),
]);
$user1 = new UserContext(25, 4, 500); // 满足第一个条件
$user2 = new UserContext(30, 1, 15000); // 满足第二个条件
$user3 = new UserContext(70, 5, 500); // 都不满足
echo 'User1 eligible: ' . ($rule->interpret($user1) ? 'true' : 'false') . PHP_EOL; // true
echo 'User2 eligible: ' . ($rule->interpret($user2) ? 'true' : 'false') . PHP_EOL; // true
echo 'User3 eligible: ' . ($rule->interpret($user3) ? 'true' : 'false') . PHP_EOL; // false三、适用场景
| 场景 | 说明 |
|---|---|
| DSL | 领域特定语言 |
| 规则引擎 | 业务规则评估 |
| 表达式求值 | 计算器、公式 |
| 查询语言 | SQL 解析 |
| 正则表达式 | 模式匹配 |
| 配置解析 | 配置文件解释 |
四、优缺点分析
| 优点 | 缺点 |
|---|---|
| 易于扩展文法 | 复杂文法类爆炸 |
| 灵活组合规则 | 性能较差 |
| 符合开闭原则 | 不适合复杂语法 |
| 易于实现简单 DSL | 调试困难 |
| 与组合模式结合 | 维护成本高 |
五、常见踩坑与问题排查
5.1 文法过复杂
php
<?php
// 问题:复杂文法导致表达式类过多
// 解决:使用解析器生成器(如 ANTLR、PHPEG.js)
// 简化:用访问者模式替代解释器
class Evaluator
{
public function evaluate(ASTNode $node): float
{
switch ($node->type) {
case 'Number':
return $node->value;
case 'Add':
return $this->evaluate($node->left) + $this->evaluate($node->right);
// ...
}
}
}5.2 性能问题
php
<?php
// 问题:每次解释都遍历语法树,性能差
// 解决:编译为闭包,缓存结果
class CompiledExpression
{
/** @var callable(Context): bool */
private $fn;
public function __construct(BooleanExpression $expr)
{
// 编译为闭包
$this->fn = fn(Context $ctx) => $expr->interpret($ctx);
}
public function evaluate(Context $ctx): bool
{
return ($this->fn)($ctx);
}
}5.3 递归过深
php
<?php
// 问题:嵌套表达式过深导致栈溢出
// 解决:使用迭代代替递归,或限制深度
class SafeInterpreter
{
private int $maxDepth = 100;
private int $currentDepth = 0;
private MathContext $context;
public function interpret(Expression $expr): float
{
if (++$this->currentDepth > $this->maxDepth) {
throw new RuntimeException('Max depth exceeded');
}
$result = $expr->interpret($this->context);
$this->currentDepth--;
return $result;
}
}六、优化方案与进阶
6.1 结合解析器
php
<?php
// 解析器:将字符串转为语法树
class ExpressionParser
{
public function parse(string $input): Expression
{
$tokens = $this->tokenize($input);
return $this->parseExpression($tokens);
}
private function tokenize(string $input): array
{
preg_match_all('/\d+|[+\-*\/()]|[a-z]+/', $input, $matches);
return $matches[0];
}
private function parseExpression(array $tokens): Expression
{
// 简化:递归下降解析
// ...
return new NumberExpression(0);
}
}
// 使用
$parser = new ExpressionParser();
$expr = $parser->parse('3 + 5 * 2');
$result = $expr->interpret(new MathContext());6.2 字节码解释
php
<?php
// 编译为字节码,再解释执行
class BytecodeInterpreter
{
private array $code = [];
public function compile(Expression $expr): void
{
// 编译为字节码
}
public function execute(Context $ctx): float
{
// 解释字节码
$stack = [];
foreach ($this->code as $op) {
switch ($op) {
case 0x01:
array_push($stack, 1);
break; // PUSH 1
// ...
}
}
return $stack[0];
}
}七、全文总结
解释器模式的核心是 定义文法表示,递归解释语言中的句子。
核心要点:
- 抽象表达式声明 interpret,终结符表示最小单元,非终结符组合表达式
- 适用于 DSL、规则引擎、表达式求值等场景
- 复杂文法会导致类爆炸,考虑使用访问者模式或解析器生成器
- 性能敏感场景需编译为函数或字节码
- 是编译原理在设计模式中的应用
