一、项目背景详细介绍

在软件工程中,我们经常会遇到这样一类问题:需要对某种“语言”或“规则”进行解析和执行。这里的语言并不一定是像 C++、Java 这样的通用编程语言,它可能是:

  • 数学表达式(如:a + b - c)

  • 规则引擎中的条件表达式(如:age > 18 && vip == true)

  • SQL 的子集

  • 正则表达式

  • 配置文件中的规则描述语言(DSL)

如果每次遇到这种需求都通过大量 if-else 或 switch 来硬编码解析逻辑,代码将迅速变得:

  • 难以维护

  • 难以扩展

  • 可读性极差

解释器模式(Interpreter Pattern) 正是为了解决这类问题而诞生的。

解释器模式属于 行为型设计模式,它的核心思想是:

将一种语言的文法表示为一个类层次结构,并使用这些类来解释语言中的句子。

在本项目中,我们将使用 C++ 实现一个简易数学表达式解释器,用于解析并计算类似如下的表达式:

a + b - c

通过该示例,你将完整理解解释器模式在真实代码中的结构和落地方式。


二、项目需求详细介绍

本项目的功能需求如下:

  1. 支持基本的变量表达式(如:a、b、c)

  2. 支持加法运算(+)

  3. 支持减法运算(-)

  4. 使用上下文(Context)存储变量与其对应的值

  5. 使用解释器模式的标准结构进行设计

  6. 代码结构清晰,符合教学与博客使用场景

非功能性需求:

  • 代码必须具备良好的可读性

  • 各个类职责清晰

  • 易于扩展(如未来增加乘法、除法)


三、相关技术详细介绍

3.1 解释器模式核心角色

解释器模式通常包含以下几个核心角色:

1. AbstractExpression(抽象表达式)

  • 声明一个解释操作 interpret()

  • 所有具体表达式的父类

2. TerminalExpression(终结符表达式)

  • 实现与文法中的终结符相关的解释操作

  • 本例中:变量表达式

3. NonTerminalExpression(非终结符表达式)

  • 表示语法规则中的非终结符

  • 本例中:加法表达式、减法表达式

4. Context(上下文)

  • 保存解释器之外的一些全局信息

  • 本例中:变量与其对应的值


3.2 C++ 面向对象特性

本项目将使用到以下 C++ 技术点:

  • 抽象基类(纯虚函数)

  • 多态

  • 智能指针(std::shared_ptr)

  • STL 容器(std::map、std::string)


四、实现思路详细介绍

整体实现思路如下:

  1. 定义一个抽象表达式类 Expression

  2. 所有表达式都继承自 Expression,并实现 interpret() 方法

  3. 使用 Context 保存变量与值的映射关系

  4. 通过组合(而非继承)来构建复杂表达式树

  5. 最终通过调用根节点的 interpret() 方法完成解释和计算

表达式:

a + b - c

其对应的抽象语法树结构为:

        (-)
       /   \
     (+)    c
    /   \
   a     b

五、完整实现代码

说明:所有代码放在一个代码块中,不同文件使用注释区分

/************************************************
 * 文件:Expression.h
 ************************************************/
#include <iostream>
#include <string>
#include <map>
#include <memory>

class Context {
private:
    std::map<std::string, int> variables;
public:
    void setVariable(const std::string& name, int value) {
        variables[name] = value;
    }

    int getVariable(const std::string& name) const {
        auto it = variables.find(name);
        if (it != variables.end()) {
            return it->second;
        }
        throw std::runtime_error("Variable not found: " + name);
    }
};

// 抽象表达式
class Expression {
public:
    virtual ~Expression() {}
    virtual int interpret(const Context& context) const = 0;
};

/************************************************
 * 文件:TerminalExpression.h
 ************************************************/
class VariableExpression : public Expression {
private:
    std::string name;
public:
    explicit VariableExpression(const std::string& varName)
        : name(varName) {}

    int interpret(const Context& context) const override {
        return context.getVariable(name);
    }
};

/************************************************
 * 文件:NonTerminalExpression.h
 ************************************************/
class AddExpression : public Expression {
private:
    std::shared_ptr<Expression> left;
    std::shared_ptr<Expression> right;
public:
    AddExpression(std::shared_ptr<Expression> l,
                  std::shared_ptr<Expression> r)
        : left(l), right(r) {}

    int interpret(const Context& context) const override {
        return left->interpret(context) + right->interpret(context);
    }
};

class SubtractExpression : public Expression {
private:
    std::shared_ptr<Expression> left;
    std::shared_ptr<Expression> right;
public:
    SubtractExpression(std::shared_ptr<Expression> l,
                       std::shared_ptr<Expression> r)
        : left(l), right(r) {}

    int interpret(const Context& context) const override {
        return left->interpret(context) - right->interpret(context);
    }
};

/************************************************
 * 文件:main.cpp
 ************************************************/
int main() {
    // 创建上下文并设置变量
    Context context;
    context.setVariable("a", 10);
    context.setVariable("b", 5);
    context.setVariable("c", 3);

    // 构建表达式 a + b - c
    std::shared_ptr<Expression> exprA = std::make_shared<VariableExpression>("a");
    std::shared_ptr<Expression> exprB = std::make_shared<VariableExpression>("b");
    std::shared_ptr<Expression> exprC = std::make_shared<VariableExpression>("c");

    std::shared_ptr<Expression> addExpr =
        std::make_shared<AddExpression>(exprA, exprB);

    std::shared_ptr<Expression> subExpr =
        std::make_shared<SubtractExpression>(addExpr, exprC);

    // 解释并输出结果
    int result = subExpr->interpret(context);
    std::cout << "Result: " << result << std::endl;

    return 0;
}

六、代码详细解读(仅解读方法作用)

6.1 Context::setVariable

用于向上下文中注册变量名及其对应的值。

6.2 Context::getVariable

根据变量名获取对应的值,如果变量不存在则抛出异常。

6.3 Expression::interpret

解释器模式的核心接口,用于定义所有表达式的统一解释行为。

6.4 VariableExpression::interpret

从上下文中读取变量的值,属于终结符表达式。

6.5 AddExpression::interpret

递归解释左右子表达式,并返回它们的加法结果。

6.6 SubtractExpression::interpret

递归解释左右子表达式,并返回它们的减法结果。


七、项目详细总结

通过本项目,我们完整实现了一个基于 解释器模式 的 C++ 示例。该模式的优点包括:

  • 文法规则清晰

  • 表达式结构可扩展

  • 符合开闭原则

但其缺点也同样明显:

  • 类数量容易膨胀

  • 复杂文法实现成本较高

因此,解释器模式适合:

  • DSL

  • 规则引擎

  • 表达式计算

而不适合复杂、通用语言的解析。


八、项目常见问题及解答

Q1:为什么不用 if-else?

A:if-else 不利于扩展,违反开闭原则。

Q2:解释器模式一定要用递归吗?

A:通常是的,因为表达式天然是树结构。

Q3:是否可以不用智能指针?

A:可以,但会增加内存管理复杂度,不利于教学。


九、扩展方向与性能优化

  1. 增加乘法、除法表达式

  2. 增加括号支持

  3. 结合工厂模式构建表达式树

  4. 使用缓存机制减少重复解释

  5. 与词法分析器、语法分析器结合,构建完整 DSL

更多推荐