实战设计模式之解释器模式

概述

作为一种行为设计模式,解释器模式提供了一种方法来定义语言的文法规则,并通过这些规则解析和处理特定类型的语言句子。简单来说,解释器模式允许我们定义一个代表某种语言中语法规则的对象结构,从而能够根据这些规则理解并处理语言中的表达式。

表达式计算器是运用解释器模式的一个典型例子:想象一下,我们正在开发一个简单的数学表达式计算器,用户可以输入类似3 + (2 - 1)的表达式,并期望得到计算结果。在这种情况下,我们可以使用解释器模式来定义表达式的结构,并实现对这些表达式的解析和计算。解释器模式使得我们可以轻松地扩展新的运算符,或改变现有的运算逻辑,而无需修改已有的代码。

基本原理

在软件项目中,我们有时需要处理一些简单的语言或表达式,比如:SQL查询、正则表达式、数学表达式等。对于这些情况,我们可以使用解释器模式来构建一种机制,使得程序可以解析并执行这些表达式。

解释器模式的核心思想是:为一种语言定义其文法表示,并定义一个或多个解释器,该解释器使用这种表示来解释语言中的句子。这里的"语言"可以是非常广泛的,从简单的数学表达式到复杂的编程语言都可以适用。解释器模式主要由以下五个核心组件构成。

1、抽象表达式。声明一个抽象的解释操作,这个接口为所有具体表达式所遵循。

2、终结符表达式。实现了与文法中的终结符相关的解释操作,每个句子中的终结符都需要该类的一个实例。

3、非终结符表达式。文法中的每一条规则,都至少需要一个具体的非终结符表达式类。每个类都实现了对子表达式的解释,并存储了对下一层表达式对象的引用。

4、上下文。包含了解释器之外的一些全局信息。

5、客户端。构建表示该文法定义的语言中一个特定的句子的抽象语法树。抽象语法树由非终结符表达式和终结符表达式的对象构成,然后初始化变量并在抽象语法树中启动解释操作。

基于上面的核心组件,解释器模式的实现主要有以下四个步骤。

1、定义文法规则。首先,明确想要解释的语言的文法规则。比如:如果要解释简单的算术表达式,你的文法规则可能包括加法、减法以及数字等。

2、创建抽象表达式接口。创建一个抽象基类或接口,其中定义了一个类似Interpret的抽象方法。所有的表达式,无论是终结符还是非终结符,都将实现这个接口。

3、实现终结符表达式。对于文法中的每一个终结符(比如:数字),创建一个具体的类实现接口,这个类通常不需要其他表达式的帮助就能完成解释工作。

4、实现非终结符表达式。对于文法中的每一个非终结符(比如:加法、减法等运算符),创建一个具体的类实现接口。这个类会包含对其他表达式的引用,并通过递归调用它们的Interpret方法来完成解释工作。

5、构建抽象语法树。根据输入的句子,构造出代表该句子结构的抽象语法树。这棵树由非终结符表达式和终结符表达式的对象构成。

6、使用上下文执行解释操作。通过调用抽象语法树根节点的Interpret方法,并传入上下文对象来开始解释过程。其中,上下文对象可以用来存储全局状态或者参数。

实战解析

在下面的实战代码中,我们使用解释器模式模拟了表达式计算器的实现。

首先,我们定义了抽象基类CExpression。它声明了一个纯虚函数Interpret,用于后续所有具体表达式的解释操作。

接着,我们定义了两个具体的表达式类。CNumberExpression作为终结符表达式表示数字,CAddExpression和CSubtractExpression作为非终结符表达式分别表示加法和减法操作。

最后,在main函数中,通过创建一系列对象来构建表达式的抽象语法树。这里构建的具体表达式是60 + (9 - 3),再调用最顶层表达式的Interpret方法来计算整个表达式的值,并输出结果。

C++ 复制代码
#include <iostream>

using namespace std;

// 抽象表达式
class CExpression
{
public:
    virtual int Interpret() const = 0;
    virtual ~CExpression() {}
};

// 终结符表达式:数字
class CNumberExpression : public CExpression
{
public:
    CNumberExpression(int nValue) : m_nValue(nValue) {}

    int Interpret() const override
    {
        return m_nValue;
    }

private:
    int m_nValue;
};

// 非终结符表达式:加法
class CAddExpression : public CExpression
{
public:
    CAddExpression(CExpression *pLeft, CExpression *pRight)
        : m_pLeft(pLeft), m_pRight(pRight) {}

    int Interpret() const override
    {
        return m_pLeft->Interpret() + m_pRight->Interpret();
    }

private:
    CExpression *m_pLeft;
    CExpression *m_pRight;
};

// 非终结符表达式:减法
class CSubtractExpression : public CExpression
{
public:
    CSubtractExpression(CExpression *pLeft, CExpression *pRight)
        : m_pLeft(pLeft), m_pRight(pRight) {}

    int Interpret() const override
    {
        return m_pLeft->Interpret() - m_pRight->Interpret();
    }

private:
    CExpression *m_pLeft;
    CExpression *m_pRight;
};

int main()
{
    // 创建子表达式: (9 - 3)
    CNumberExpression *pNum1 = new CNumberExpression(9);
    CNumberExpression *pNum2 = new CNumberExpression(3);
    CSubtractExpression *pSub = new CSubtractExpression(pNum1, pNum2);

    // 创建主表达式: 60 + (9 - 3)
    CNumberExpression *pNum3 = new CNumberExpression(60);
    CAddExpression *pResult = new CAddExpression(pNum3, pSub);

    // 执行解释操作
    int nResult = pResult->Interpret();

    // 输出结果
    cout << "Result: " << nResult << endl;

    delete pResult;
    delete pNum3;
    delete pSub;
    delete pNum2;
    delete pNum1;
    return 0;
}

总结

通过将每条语法规则映射到一个具体的类,解释器模式可以将复杂的解释逻辑分解为多个独立的部分,每个部分负责处理特定类型的语法。这样不仅使代码更易读,也更容易维护。新的语法或表达式可以通过添加新的类来实现,而不需要修改现有的代码。这使得系统更加灵活,能够适应不断变化的需求。

但当面对复杂的语言或大量数据时,解释器模式可能会导致性能瓶颈。这是因为,每次解释都需要遍历整个抽象语法树,且通常涉及大量的对象创建和递归调用。随着语言文法变得越来越复杂,相应的抽象语法树也会变得庞大且难以管理。特别是当文法规则频繁变动时,维护成本会显著增加。

相关推荐
m0_640602447 分钟前
2026 年餐饮收银系统前后端技术实现——核心架构与原理详解
后端·微服务·云原生·架构
lingran__15 分钟前
C++ STL unordered系列(哈希) 底层剖析与模拟实现万字详解 | 基于哈希表,复刻 SGI-STL 泛型哈希容器架构
开发语言·c++·后端·哈希算法·哈希表·泛型编程·unordered系列
鸿芯微控科技25 分钟前
MFC设定流量达不到怎么办?上游供气能力、压差与管路阻力排查
c++·mfc·质量流量控制器·流量不足排查·压差测试·气体流量控制
江畔柳前堤37 分钟前
AgentScope 设计与原理全解:从消息原语到分布式智能体工程底座
大数据·人工智能·分布式·目标检测·机器学习·语言模型·架构
烬羽41 分钟前
NestJS 依赖注入:Controller 里那个没有 new 的 service,到底从哪来的?
设计模式·typescript·nestjs
郝学胜_神的一滴41 分钟前
C++20 高级编程 003:吃透现代基础语法与标准容器
c++
两万五千个小时1 小时前
DeepSeek Harness 上下文压缩:长对话管理
人工智能·程序员·架构
码匠许师傅1 小时前
【C++ 面试真题】聊聊 C++ 的关联容器
开发语言·c++·面试
一拳不是超人1 小时前
一个 AI 改出的 bug,另一个 AI 五天就打穿了 Snowflake:Copilot Autofix 事件给 coding agent 的警醒
架构·github
Hhy_11072 小时前
《C++深度解构02》类和对象(上)——定义规范、内存对齐与 this 指针全解
c语言·开发语言·c++·学习·visual studio