在开发脚本引擎或配置解析工具时,我们经常需要处理多种类型的语句(赋值、条件、循环等)。本文将通过一个实用的语句解析器案例,展示如何用注册式工厂模式构建易于扩展的解析系统。

一、需求分析与设计思路

我们需要开发一个支持以下语句类型的解析器:

  • 赋值语句(如x = 100)

  • 条件语句(如if x > 5)

  • 循环语句(如for i in 0..10)

核心挑战是:当需要支持新语句类型时,无需修改现有解析逻辑,只需添加新的解析器实现。这正是工厂模式的用武之地。

整体设计方案:

  • 定义抽象解析器接口(基类)

  • 为每种语句实现具体解析器(派生类)

  • 用注册式工厂管理解析器的创建

  • 通过语句特征自动匹配对应的解析器

二、核心代码实现

1. 抽象解析器接口

首先定义所有解析器的公共接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <string>

// 语句解析器基类
class StatementParser {
public:
virtual ~StatementParser() = default;
// 解析语句(提取关键信息)
virtual void parse(const std::string& statement) = 0;
// 执行解析后的逻辑
virtual void execute() = 0;
// 获取解析器支持的语句类型标识
virtual std::string type() const = 0;
};

2. 注册式工厂实现

使用模板工厂 + 自动注册机制,让解析器可以 "自注册":

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
#include <unordered_map>
#include <functional>
#include <memory>
#include <stdexcept>

// 解析器工厂
template <typename BaseParser>
class ParserFactory {
public:
using Creator = std::function<std::unique_ptr<BaseParser>()>;

// 注册解析器
void register_parser(const std::string& type, Creator creator) {
if (creators_.count(type)) {
throw std::runtime_error("解析器类型已注册: " + type);
}
creators_[type] = std::move(creator);
}

// 创建解析器
std::unique_ptr<BaseParser> create_parser(const std::string& type) {
auto it = creators_.find(type);
if (it == creators_.end()) {
throw std::invalid_argument("未知解析器类型: " + type);
}
return it->second();
}

// 单例实例
static ParserFactory& instance() {
static ParserFactory factory;
return factory;
}

private:
ParserFactory() = default;
std::unordered_map<std::string, Creator> creators_;
};

// 自动注册辅助类
template <typename Base, typename Derived>
class AutoRegisterParser {
public:
AutoRegisterParser() {
// 利用Derived的type()获取类型标识
Derived dummy;
ParserFactory<Base>::instance().register_parser(
dummy.type(),
[](){ return std::make_unique<Derived>(); }
);
}
};

3. 具体解析器实现

为每种语句实现解析器,并通过AutoRegisterParser自动注册:

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
#include <iostream>
#include <string_view>

// 赋值语句解析器
class AssignmentParser : public StatementParser {
public:
void parse(const std::string& statement) override {
// 实际实现会提取变量名和值
size_t eq_pos = statement.find('=');
var_ = statement.substr(0, eq_pos);
value_ = statement.substr(eq_pos + 1);
std::cout << "解析赋值: " << var_ << " <- " << value_ << std::endl;
}

void execute() override {
std::cout << "执行赋值操作: " << var_ << " = " << value_ << std::endl;
}

std::string type() const override {
return "assignment";
}

private:
std::string var_;
std::string value_;
// 静态注册对象
static AutoRegisterParser<StatementParser, AssignmentParser> reg;
};
// 初始化注册器,完成自动注册
AutoRegisterParser<StatementParser, AssignmentParser> AssignmentParser::reg;


// 条件语句解析器
class ConditionParser : public StatementParser {
public:
void parse(const std::string& statement) override {
cond_ = statement.substr(3); // 跳过"if "
std::cout << "解析条件: " << cond_ << std::endl;
}

void execute() override {
std::cout << "执行条件判断: " << cond_ << std::endl;
}

std::string type() const override {
return "condition";
}

private:
std::string cond_;
static AutoRegisterParser<StatementParser, ConditionParser> reg;
};
AutoRegisterParser<StatementParser, ConditionParser> ConditionParser::reg;


// 循环语句解析器
class LoopParser : public StatementParser {
public:
void parse(const std::string& statement) override {
loop_info_ = statement.substr(4); // 跳过"for "
std::cout << "解析循环: " << loop_info_ << std::endl;
}

void execute() override {
std::cout << "执行循环: " << loop_info_ << std::endl;
}

std::string type() const override {
return "loop";
}

private:
std::string loop_info_;
static AutoRegisterParser<StatementParser, LoopParser> reg;
};
AutoRegisterParser<StatementParser, LoopParser> LoopParser::reg;

4. 语句类型识别与调度

需要一个函数根据语句特征判断类型,映射到注册的解析器:

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
// 判断语句类型
std::string detect_statement_type(const std::string& statement) {
if (statement.find('=') != std::string::npos) {
return "assignment";
} else if (statement.substr(0, 2) == "if") {
return "condition";
} else if (statement.substr(0, 3) == "for") {
return "loop";
}
return "";
}

// 解析并执行语句
void process_statement(const std::string& statement) {
try {
std::string type = detect_statement_type(statement);
if (type.empty()) {
throw std::invalid_argument("无法识别的语句: " + statement);
}
auto parser = ParserFactory<StatementParser>::instance().create_parser(type);
parser->parse(statement);
parser->execute();
} catch (const std::exception& e) {
std::cerr << "错误: " << e.what() << std::endl;
}
}

三、使用示例与扩展演示

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main() {
std::vector<std::string> statements = {
"x = 42",
"if x > 10",
"for i in 0..5",
"unknown"
};

for (const auto& stmt : statements) {
std::cout << "\n处理语句: " << stmt << std::endl;
process_statement(stmt);
}
return 0;
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
处理语句: x = 42
解析赋值: x <- 42
执行赋值操作: x = 42

处理语句: if x > 10
解析条件: x > 10
执行条件判断: x > 10

处理语句: for i in 0..5
解析循环: i in 0..5
执行循环: i in 0..5

处理语句: unknown
错误: 无法识别的语句: unknown

新增解析器的便捷性

要支持打印语句(如print "hello"),只需添加:

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
class PrintParser : public StatementParser {
public:
void parse(const std::string& statement) override {
content_ = statement.substr(6); // 跳过"print "
std::cout << "解析打印: " << content_ << std::endl;
}

void execute() override {
std::cout << "执行打印: " << content_ << std::endl;
}

std::string type() const override {
return "print";
}

private:
std::string content_;
static AutoRegisterParser<StatementParser, PrintParser> reg;
};
AutoRegisterParser<StatementParser, PrintParser> PrintParser::reg;

// 同时更新类型识别函数
std::string detect_statement_type(const std::string& statement) {
// ... 原有逻辑 ...
else if (statement.substr(0, 5) == "print") {
return "print";
}
return "";
}

无需修改工厂或其他解析器代码,即可支持新语句类型,完美体现了 "开闭原则"。

四、总结

通过注册式工厂模式实现的语句解析器,具有以下优势:

  • 新增语句类型只需添加解析器类,无需修改核心框架

  • 解析逻辑与类型识别分离,职责清晰

  • 利用 C++ 模板和静态初始化实现自动注册,减少重复代码

  • 客户端代码与具体解析器解耦,专注于业务逻辑

这种设计特别适合需要持续扩展语法的场景,如脚本引擎、配置文件解析器、数据格式转换器等。掌握注册式工厂模式,能显著提升代码的可扩展性和可维护性。