初识设计模式——解释器模式(Interpreter Pattern)

cuixiaogang

解释器模式(Interpreter Pattern)是一种行为设计模式,它定义了一种语言的文法表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。简单来说,就是为了解释一种特定的语言而设计的模式。

解释器模式的构成

  • 抽象表达式(Abstract Expression):定义解释操作的抽象接口,具体的解释器类将实现该接口。
  • 终结符表达式(Terminal Expression):实现与文法中的终结符相关的解释操作。终结符是语言中不能再分解的基本符号。
  • 非终结符表达式(Non - Terminal Expression):实现与文法中的非终结符相关的解释操作。非终结符是- **可以由其他符号(终结符或非终结符)组成的符号。
  • 上下文(Context):包含解释器解释时需要的全局信息。
  • 客户端(Client):构建表示该语言中某个特定句子的抽象语法树,并调用解释操作。

解释器模式的结构图

示例

场景介绍

开发中,经常需要使用配置文件来管理一些经常变化的内容,假定配置文件采用简单的键值对格式,像key = value这样,并且支持注释(以 # 开头)。我们要实现一个解释器,对配置文件内容进行解析,把键值对提取出来。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
<?php
// 抽象表达式
abstract class ConfigExpression {
abstract public function interpret($context);
}

// 终结符表达式:键值对
class KeyValueExpression extends ConfigExpression {
private $key;
private $value;

public function __construct($key, $value) {
$this->key = $key;
$this->value = $value;
}

public function interpret($context) {
$context[$this->key] = $this->value;
return $context;
}
}

// 非终结符表达式:注释,忽略
class CommentExpression extends ConfigExpression {
public function interpret($context) {
return $context;
}
}

// 非终结符表达式:空行,忽略
class EmptyLineExpression extends ConfigExpression {
public function interpret($context) {
return $context;
}
}

// 上下文
class ConfigContext {
private $config = [];

public function getConfig() {
return $this->config;
}

public function setConfig($config) {
$this->config = $config;
}
}

// 解释器
class ConfigInterpreter {
public function parse($lines) {
$context = new ConfigContext();
foreach ($lines as $line) {
$expression = $this->createExpression($line);
$context->setConfig($expression->interpret($context->getConfig()));
}
return $context->getConfig();
}

private function createExpression($line) {
$line = trim($line);
if (strpos($line, '#') === 0) {
return new CommentExpression();
} elseif (empty($line)) {
return new EmptyLineExpression();
} else {
list($key, $value) = explode('=', $line, 2);
$key = trim($key);
$value = trim($value);
return new KeyValueExpression($key, $value);
}
}
}

// 读取配置文件
$configFile = 'config.txt';
if (file_exists($configFile)) {
$lines = file($configFile, FILE_IGNORE_NEW_LINES);
$interpreter = new ConfigInterpreter();
$config = $interpreter->parse($lines);

// 输出配置
foreach ($config as $key => $value) {
echo "$key: $value\n";
}
} else {
echo "配置文件未找到。";
}
?>

代码含义

  • 抽象表达式(ConfigExpression):定义了解释操作的抽象接口 interpret。
  • 终结符表达式(KeyValueExpression):对键值对进行解释,将其添加到配置上下文中。
  • 非终结符表达式(CommentExpression 和 EmptyLineExpression):分别处理注释和空行,直接返回上下文,忽略这些行。
  • 上下文(ConfigContext):用来存储解析后的配置信息。
  • 解释器(ConfigInterpreter):读取配置文件的每一行,创建对应的表达式对象,然后调用 interpret 方法进行解释。

UML类图

UML类图

解释器模式的优缺点

优点

  • 可扩展性:可以很容易地添加新的解释器规则,扩展语言的功能。
  • 灵活性:可以根据不同的需求,灵活地组合和使用解释器。
  • 可维护性:每个解释器类只负责一个特定的解释任务,代码结构清晰,易于维护。

缺点

  • 复杂性:对于复杂的文法,解释器模式会导致类的数量急剧增加,使代码变得复杂和难以维护。
  • 效率问题:解释器模式通常需要递归调用,对于大规模的输入,会导致性能问题。

解释器模式的常用应用场景

  • 编译器:编译器需要对源代码进行词法分析、语法分析和语义分析,解释器模式可以用于实现语法分析阶段,将源代码解析为抽象语法树。
  • 正则表达式引擎:正则表达式是一种特定的语言,解释器模式可以用于解释和执行正则表达式。
  • SQL 解析器:解析 SQL 语句,将其转换为数据库可以执行的操作。
  • 配置文件解析:解析配置文件中的特定语法,例如 XML、JSON 等。