在开发脚本引擎或配置解析工具时,我们经常需要处理多种类型的语句(赋值、条件、循环等)。本文将通过一个实用的语句解析器案例,展示如何用注册式工厂模式构建易于扩展的解析系统。
一、需求分析与设计思路
我们需要开发一个支持以下语句类型的解析器:
赋值语句(如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 ""; }
|
无需修改工厂或其他解析器代码,即可支持新语句类型,完美体现了 "开闭原则"。
四、总结
通过注册式工厂模式实现的语句解析器,具有以下优势:
这种设计特别适合需要持续扩展语法的场景,如脚本引擎、配置文件解析器、数据格式转换器等。掌握注册式工厂模式,能显著提升代码的可扩展性和可维护性。