23.设计模式-解释器模式

解释器模式(interpreter),给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

需求

  • O表示音阶'O 1'表示低音阶,'O 2'表示中音阶,'O 3'表示高音阶;
  • 'P '表示休止符,
  • 'C D E F G A B '表示'Do-Re-Mi-Fa-So-La-Ti';
  • 音符长度 1表示一拍,2表示二拍,0.5表示半拍,0.25表示四分之一拍,以此类推;

注意:所有的字母和数字都要用半角空格分开。例如上海滩的歌曲第一句,'浪奔',可以写成'O 2 E 0.5 G 0.5 A 3 '表示中音开始,演奏的是mi so la。

为了只关注设计模式编程,而不是具体的播放实现,你只需要用控制台根据事先编写的语句解释成简谱就成了。

代码

业务类

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Expression {
    void (*interpret)(struct Expression *, char **);
    void (*excecute)(char, double);
} Expression;

void ExpressionInterpret(Expression *obj, char **plainText) {
    int n = strlen(*plainText);
    if(n == 0) {
        return;
    }
    else {
        char playKey = (*plainText)[0];
        int cur = 2;
        while(cur < n ) {
            if((*plainText)[cur] == ' ') {
                break;
            }
            cur++;
        }
        char value[4];
        strncpy(value, *plainText+2, cur-2);
        value[cur-2] = '\0';
        double playValue;
        sscanf(value, "%lf", &playValue);
        obj->excecute(playKey, playValue);
        *plainText += cur+1;
    }
}
// 音阶
typedef struct Note {
    Expression base;
} Note;

void NoteExcecute(char key, double value) {
    char note;
    if(key >= 'C' && key <= 'G') {
        note = key-'C'+'1';
    }
    else if(key >= 'A' && key <= 'B') {
        note = key-'A'+'6';
    }
    printf("%c ", note);
}

Expression *InitNote() {
    Note *obj = (Note *)malloc(sizeof(Note));
    obj->base.excecute = NoteExcecute;
    obj->base.interpret = ExpressionInterpret;
    return (Expression *)obj;
}
// 音阶
typedef struct Scale {
    Expression base;    
} Scale;

void ScaleExcecute(char key, double value) {
    char *scale = NULL;
    int v = (int)value;
    switch (v)
    {
    case 1:
        scale = "低音";
        break;
    case 2:
        scale = "中音";
        break;
    case 3:
        scale = "高音";
        break;
    default:
        break;
    }
    printf("%s ", scale);
}

Expression *InitScale() {
    Scale *obj = (Scale *)malloc(sizeof(Scale));
    obj->base.excecute = ScaleExcecute;
    obj->base.interpret = ExpressionInterpret;
    return (Expression *)obj;
}

客户端

c 复制代码
int main() {
    char *text = "O 2 E 0.5 G 0.5 A 3 E 0.5 G 0.5 D 3 E 0.5 G 0.5 A 0.5 O 3 C 1 O 2 A 0.5 G 1 C 0.5 E 0.5 D 3";
    int n = strlen(text);
    Expression *exp = NULL;
    char *tmp = text;
    while(tmp-text < n) {
        switch (tmp[0])
        {
            case 'O':
                exp = InitScale();
                break;
            case 'C':
            case 'D':
            case 'E':
            case 'F':
            case 'G':
            case 'A':
            case 'B':
                exp = InitNote();
                break;
            default:
                break;
        }
        exp->interpret(exp, &tmp);
        free(exp);
    }
    printf("\n");
    return 0;
}

UML图

总结

  • 解释器模式使用场景?

    如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

    如当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。

  • 解释器模式的优点?

    用了解释器模式,就意味着可以很容易地改变和扩展文法,因为该模式使用类来表示文法规则,你可使用继承来改变或扩展该文法。也比较容易实现文法,因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写。

  • 解释器模式的缺点?

    解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。建议当文法非常复杂时,使用其他的技术如语法分析程序或编译器生成器来处理。

正则表达式就是解释器模式思想的一个实际使用场景。

相关推荐
阿闽ooo3 天前
中介者模式打造多人聊天室系统
c++·设计模式·中介者模式
小米4963 天前
js设计模式 --- 工厂模式
设计模式
逆境不可逃3 天前
【从零入门23种设计模式08】结构型之组合模式(含电商业务场景)
线性代数·算法·设计模式·职场和发展·矩阵·组合模式
驴儿响叮当20103 天前
设计模式之状态模式
设计模式·状态模式
电子科技圈3 天前
XMOS推动智能音频等媒体处理技术从嵌入式系统转向全新边缘计算
人工智能·mcu·物联网·设计模式·音视频·边缘计算·iot
徐先生 @_@|||3 天前
安装依赖三方exe/msi的软件设计模式
设计模式
希望_睿智4 天前
实战设计模式之访问者模式
c++·设计模式·架构
茶本无香4 天前
设计模式之十六:状态模式(State Pattern)详解 -优雅地管理对象状态,告别繁琐的条件判断
java·设计模式·状态模式
驴儿响叮当20104 天前
设计模式之备忘录模式
设计模式·备忘录模式
驴儿响叮当20104 天前
设计模式之迭代器模式
设计模式·迭代器模式