《设计模式的艺术》笔记 - 解释器模式

介绍

解释器模式定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的"语言"是指使用规定格式和语法的代码。解释器模式是一种类行为型模式。

实现

myclass.h

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#ifndef DESIGNPATTERNS_MYCLASS_H
#define DESIGNPATTERNS_MYCLASS_H

#include <iostream>
#include <unordered_map>
#include <atomic>
#include <vector>
#include <memory>

class Context {

};

class AbstractExpression { // 抽象表达式
public:
    virtual float interpret() = 0;
};

class Add : public AbstractExpression {    // 非终结符表达式
public:
    Add(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right);

    float interpret() override;

private:
    std::shared_ptr<AbstractExpression> m_left;
    std::shared_ptr<AbstractExpression> m_right;
};

class Substract : public AbstractExpression {    // 非终结符表达式
public:
    Substract(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right);

    float interpret() override;

private:
    std::shared_ptr<AbstractExpression> m_left;
    std::shared_ptr<AbstractExpression> m_right;
};

class Multiply : public AbstractExpression {    // 非终结符表达式
public:
    Multiply(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right);

    float interpret() override;

private:
    std::shared_ptr<AbstractExpression> m_left;
    std::shared_ptr<AbstractExpression> m_right;
};

class Division : public AbstractExpression {    // 非终结符表达式
public:
    Division(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right);

    float interpret() override;

private:
    std::shared_ptr<AbstractExpression> m_left;
    std::shared_ptr<AbstractExpression> m_right;
};

class Negative : public AbstractExpression {    // 非终结符表达式
public:
    Negative(std::shared_ptr<AbstractExpression> exp);

    float interpret() override;

private:
    std::shared_ptr<AbstractExpression> m_exp;
};

class Value : public AbstractExpression {
public:
    Value(float val);

    float interpret() override;

private:
    float m_val;
};

class ExpressionParser {
public:
    ExpressionParser(const std::string &expStr);

    std::shared_ptr<AbstractExpression> getExpression();

private:
    void popOperation();

    std::vector<char> m_ops;
    std::vector<std::shared_ptr<AbstractExpression>> m_vals;
};

#endif //DESIGNPATTERNS_MYCLASS_H

myclass.cpp

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#include "myclass.h"
#include <thread>
#include <unistd.h>

Add::Add(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right) {
    m_left = left;
    m_right = right;
}

float Add::interpret() {
    return m_left->interpret() + m_right->interpret();
}

Substract::Substract(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right) {
    m_left = left;
    m_right = right;
}

float Substract::interpret() {
    return m_left->interpret() - m_right->interpret();
}

Multiply::Multiply(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right) {
    m_left = left;
    m_right = right;
}

float Multiply::interpret() {
    return m_left->interpret() * m_right->interpret();
}

Division::Division(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right) {
    m_left = left;
    m_right = right;
}

float Division::interpret() {
    return m_left->interpret() / m_right->interpret();
}

Negative::Negative(std::shared_ptr<AbstractExpression> exp) {
    m_exp = exp;
}

float Negative::interpret() {
    return -m_exp->interpret();
}

Value::Value(float val) {
    m_val = val;
}

float Value::interpret() {
    return m_val;
}

ExpressionParser::ExpressionParser(const std::string &expStr) {
    char prev = '\0', c, op;
    std::shared_ptr<AbstractExpression> exp;
    std::unordered_map<char, char> brackets = {{'}', '{'}, {']', '['}, {')', '('}};
    float val;

    for (int i = 0; i < expStr.length(); ++i) {
        c = expStr[i];
        if (c == ' ') {
            continue;
        } else if (c == '+') {
            while (m_ops.size() > 0 && m_ops[m_ops.size() - 1] != '{' && m_ops[m_ops.size() - 1] != '[' && m_ops[m_ops.size() - 1] != '(') {
                popOperation();
            }
            m_ops.push_back(c);
            prev = c;
        } else if (c == '-') {
            if (prev == '\0' || prev == '{' || prev == '[' || prev == '(') {
                m_vals.push_back(0);
            } else {
                while (m_ops.size() > 0 && m_ops[m_ops.size() - 1] != '{' && m_ops[m_ops.size() - 1] != '[' && m_ops[m_ops.size() - 1] != '(') {
                    popOperation();
                }
            }
            m_ops.push_back(c);
            prev = c;
        } else if (c == '*' || c == '/') {
            if (m_ops.size() > 0 && (m_ops[m_ops.size() - 1] == '*' || m_ops[m_ops.size() - 1] == '/')) {
                popOperation();
            }
            m_ops.push_back(c);
            prev = c;
        } else if (c == '{' || c == '[' || c == '(') {
            m_ops.push_back(c);
            prev = c;
        } else if (c == '}' || c == ']' || c == ')') {
            while (m_ops.size() > 0) {
                op = m_ops[m_ops.size() - 1];
                if (op == brackets[c]) {
                    m_ops.pop_back();
                    break;
                } else {
                    popOperation();
                }
            }
        } else if (c >= '0' && c <= '9'){
            char buf[128];
            int j = 0;
            while (c == '.' || (c >= '0' && c <= '9')) {
                buf[j] = c;
                j++;
                i++;
                c = expStr[i];
            }
            prev = '0';
            --i;
            buf[j] = '\0';
            val = atof(buf);
            exp.reset(new Value(val));
            m_vals.push_back(exp);
        }
    }

    if (m_ops.size() > 0) {
        popOperation();
    }
    if (m_vals.size() != 1) {
        goto failed;
    }
    return;

failed:
    m_vals.resize(1);
    m_vals[0] = nullptr;
}

void ExpressionParser::popOperation() {
    char op = m_ops[m_ops.size() - 1];
    std::shared_ptr<AbstractExpression> exp;

    if (op == '+') {
        exp.reset(new Add(m_vals[m_vals.size() - 2], m_vals[m_vals.size() - 1]));
    } else if (op == '-') {
        exp.reset(new Substract(m_vals[m_vals.size() - 2], m_vals[m_vals.size() - 1]));
    } else if (op == '*') {
        exp.reset(new Multiply(m_vals[m_vals.size() - 2], m_vals[m_vals.size() - 1]));
    } else if (op == '/') {
        exp.reset(new Division(m_vals[m_vals.size() - 2], m_vals[m_vals.size() - 1]));
    } else {
        return;
    }
    m_ops.pop_back();
    m_vals.pop_back();
    m_vals.pop_back();
    m_vals.push_back(exp);
}

std::shared_ptr<AbstractExpression> ExpressionParser::getExpression() {
    return m_vals[0];
}

main.cpp

cpp 复制代码
#include <iostream>
#include <mutex>
#include "myclass.h"

int main() {
    ExpressionParser *expressionParser = new ExpressionParser("(1+2)*(6-2)/2+1");
    auto exp = expressionParser->getExpression();
    if (exp) {
        std::cout << exp->interpret() << std::endl;
    } else {
        std::cout << 0 << std::endl;
    }

    return 0;
}

总结

优点

  1. 易于改变和扩展文法。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。

  2. 每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。

  3. 实现文法较为容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些工具自动生成节点类代码。

  4. 增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合开闭原则。

缺点

  1. 对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一种语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。

  2. 执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。

适用场景

  1. 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。

  2. 一些重复出现的问题可以用一种简单的语言来进行表达。

  3. 一个语言的文法较为简单。

  4. 执行效率不是关键问题。(注:高效的解释器通常不是通过直接解释抽象语法树来实现的,而是需要将它们转换成其他形式,使用解释器模式的执行效率并不高。)

练习

myclass.h

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#ifndef DESIGNPATTERNS_MYCLASS_H
#define DESIGNPATTERNS_MYCLASS_H

#include <iostream>
#include <unordered_map>
#include <atomic>
#include <vector>
#include <memory>

/**
 * COPY VIEW FROM srcDB TO desDB 表示将数据库srcDB中的所有视图对象都复制至数据库desDB
 * MOVE TABLE Student FROM srcDB TO desDB 表示将数据库srcDB中的Student表移动至数据库desDB
 * expression ::= operation object FROM string to string
 * operation ::= 'COPY' | 'MOVE'
 * object ::= 'VIEW' | 'TABLE string'
 */

class AbstractNode {
public:
    virtual std::string interpret() = 0;
};

class SentenceNode : public AbstractNode {  // expression 简单句子,非终结符表达式
public:
    SentenceNode(std::shared_ptr<AbstractNode> operation, std::shared_ptr<AbstractNode> object, std::string &srcDB, std::string &desDB);

    std::string interpret() override;

private:
    std::shared_ptr<AbstractNode> m_operation;
    std::shared_ptr<AbstractNode> m_object;
    std::string m_srcDB;
    std::string m_desDB;
};

class OperationNode : public AbstractNode { // 终结符表达式
public:
    OperationNode(std::string operation);
    std::string interpret() override;
private:
    std::string m_operation;
};

class ObjectNode : public AbstractNode {    // 终结符表达式
public:
    ObjectNode(std::string object, std::string name = "");

    std::string interpret() override;

private:
    std::string m_object;
    std::string m_name;
};

class InstructionHandler {
public:
    void handle(std::string &instruction);

    std::string output();

private:
    std::shared_ptr<AbstractNode> m_node;
};

#endif //DESIGNPATTERNS_MYCLASS_H

myclass.cpp

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#include "myclass.h"
#include <thread>
#include <unistd.h>
#include <sstream>


SentenceNode::SentenceNode(std::shared_ptr<AbstractNode> operation, std::shared_ptr<AbstractNode> object,
                           std::string &srcDB, std::string &desDB) {
    m_operation = operation;
    m_object = object;
    m_srcDB = srcDB;
    m_desDB = desDB;
}

std::string SentenceNode::interpret() {
    return "将" + m_srcDB + "的" + m_object->interpret() + m_operation->interpret() + "到" + m_desDB;
}

OperationNode::OperationNode(std::string operation) {
    m_operation = operation;
}

std::string OperationNode::interpret() {
    if (m_operation == "COPY") {
        return "拷贝";
    } else if (m_operation == "MOVE") {
        return "移动";
    } else {
        return "无效指令";
    }
}

ObjectNode::ObjectNode(std::string object, std::string name) {
    m_object = object;
    m_name = name;
}

std::string ObjectNode::interpret() {
    if (m_object == "VIEW") {
        return "视图";
    } else if (m_object == "TABLE") {
        return m_name + "表";
    } else {
        return "无效指令";
    }
}

void InstructionHandler::handle(std::string &instruction) {
    std::istringstream iss(instruction);
    std::string token, token2, srcDB, desDB;
    std::shared_ptr<AbstractNode> op, obj;
    int i = 0;
    while (iss >> token) {
        if (token == "TO") {
            continue;
        } else if (token == "FROM") {
            if (i == 1) {
                obj.reset(new ObjectNode(token2, ""));
                ++i;
            }
            continue;
        }
        if (i == 0) {
            op.reset(new OperationNode(token));
            ++i;
        } else if (i == 1) {
            if (token2.length() <= 0) {
                token2 = token;
                continue;
            } else {
                obj.reset(new ObjectNode(token2, token));
                ++i;
            }
        } else if (i == 2){
            srcDB = token;
            ++i;
        } else if (i == 3) {
            desDB = token;
            ++i;
        }
    }
    m_node.reset(new SentenceNode(op, obj, srcDB, desDB));
}

std::string InstructionHandler::output() {
    return m_node->interpret();
}

main.cpp

cpp 复制代码
#include <iostream>
#include <mutex>
#include "myclass.h"

int main() {
    InstructionHandler *instructionHandler = new InstructionHandler();
    std::string ins = "COPY VIEW FROM AA_DB TO BB_DB";
    instructionHandler->handle(ins);
    std::cout << instructionHandler->output() << std::endl;

    ins = "COPY TABLE student FROM AA_DB TO BB_DB";
    instructionHandler->handle(ins);
    std::cout << instructionHandler->output() << std::endl;

    delete instructionHandler;
    return 0;
}
相关推荐
冷眼看人间恩怨5 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
思忖小下10 小时前
梳理你的思路(从OOP到架构设计)_简介设计模式
设计模式·架构·eit
Hejjon10 小时前
SpringBoot 整合 SQLite 数据库
笔记
liyinuo201712 小时前
嵌入式(单片机方向)面试题总结
嵌入式硬件·设计模式·面试·设计规范
西洼工作室13 小时前
【java 正则表达式 笔记】
java·笔记·正则表达式
初学者7.13 小时前
Webpack学习笔记(2)
笔记·学习·webpack
aaasssdddd9614 小时前
C++的封装(十四):《设计模式》这本书
数据结构·c++·设计模式
T1an-114 小时前
设计模式之【观察者模式】
观察者模式·设计模式
新手上路狂踩坑14 小时前
Android Studio的笔记--BusyBox相关
android·linux·笔记·android studio·busybox
stm 学习ing15 小时前
HDLBits训练3
c语言·经验分享·笔记·算法·fpga·eda·verilog hdl