zoukankan      html  css  js  c++  java
  • 大话设计模式笔记(二十四)の解释器模式

    解释器模式

    定义

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

    目的

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

    UML图

    好处

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

    不足

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

    举个栗子

    实现一个简单的音乐解释器。规则大致有:“C D E F G A B”分别表示“Do-Re-Mi-Fa-So-La-Ti”;音符长度1表示一拍,2表示二拍,0.5表示半拍,0.25表示四分之一拍,以此类推。解释以下《上海滩》

    Talk is cheap, show me the code

    (屁话少说,放码过来)

    /**
     * 演奏内容类
     * Created by callmeDevil on 2019/12/15.
     */
    public class PlayContext {
    
        // 演奏文本
        private  String text;
    
        // 省略 get set
    }
    
    /**
     * 表达式类
     * Created by callmeDevil on 2019/12/15.
     */
    public abstract class Expression {
    
        // 解释器
        public void interpret(PlayContext context){
            if (context.getText().length() == 0) {
                return;
            } else {
                // 此方法用于将当前演奏文本第一条命令获得命令字母和其参数值
                // 例如:“O 3 E 0.5 G 0.5 A 3”,则 playKey 为O,而playValue 为3
                String playKey = context.getText().substring(0, 1);
                context.setText(context.getText().substring(2));
                double playValue = Double.parseDouble(context.getText().substring(0, context.getText().indexOf(" ")));
                // 获得playKey 和 playValue 后将其从演奏文本中移除
                // 例如:“O 3 E 0.5 G 0.5 A 3”变成了“E 0.5 G 0.5 A 3”
                context.setText(context.getText().substring(context.getText().indexOf(" ") + 1));
    
                excute(playKey, playValue);
            }
        }
    
        // 抽象方法执行,不同的文法的子类,有不同的执行处理
        public abstract void excute(String key ,double value);
    
    }
    
    /**
     * 音符类
     * Created by callmeDevil on 2019/12/15.
     */
    public class Note extends Expression {
        @Override
        public void excute(String key, double value) {
            String note = "";
            switch (key) {
                case "C":
                    note = "1"; // 如果获得的 key 是C,则演奏1(do),是D则演奏2(Re),以此类推
                    break;
                case "D":
                    note = "2";
                    break;
                case "E":
                    note = "3";
                    break;
                case "F":
                    note = "4";
                    break;
                case "G":
                    note = "5";
                    break;
                case "A":
                    note = "6";
                    break;
                case "B":
                    note = "7";
                    break;
            }
            System.out.print(note + " ");
        }
    }
    
    /**
     * 音阶类
     * Created by callmeDevil on 2019/12/15.
     */
    public class Scale extends Expression {
        @Override
        public void excute(String key, double value) {
            String scale = "";
            switch ((int) value) {
                case 1 :
                    scale = "低音";
                    break;
                case 2:
                    scale = "中音";
                    break;
                case 3:
                    scale = "高音";
                    break;
            }
            System.out.print(scale + " ");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            PlayContext context = new PlayContext();
            // 音乐-上海滩
            System.out.println("上海滩:");
            context.setText("O 2 E 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 ");
            Expression expression = null;
            try {
                while (context.getText().length() > 0) {
                    String string = context.getText().substring(0, 1);
                    switch (string) {
                        case "O": // 当首字母是O时,表达式实例化为音阶
                            expression = new Scale();
                            break;
                        case "C":
                        case "D":
                        case "E":
                        case "F":
                        case "G":
                        case "A":
                        case "B":
                        case "P": // 当首字母是CDEFGAB以及休止符P时,则实例化为音符
                            expression = new Note();
                            break;
                    }
                    expression.interpret(context);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    运行结果

    上海滩:
    中音 3 6 3 5 2 3 5 6 高音 1 中音 6 5 1 3 2 
    

    需求变更

    增加一个文法,就是演奏速度,用“T”表示,毫秒为单位,‘T 1000’表示每节拍一秒。

    增加一个子类

    /**
     * 音速类
     * Created by callmeDevil on 2019/12/15.
     */
    public class Speed extends Expression{
        @Override
        public void excute(String key, double value) {
            String speed;
            if (value < 500) {
                speed = "快速";
            } else if (value >= 1000) {
                speed = "慢速";
            } else {
                speed = "中速";
            }
            System.out.print(speed + " ");
        }
    }
    

    客户端改变

    public class Test {
        public static void main(String[] args) {
            PlayContext context = new PlayContext();
            // 音乐-上海滩
            System.out.println("上海滩:");
            // 增加速度的设置
            context.setText("T 500 O 2 E 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 ");
            Expression expression = null;
            try {
                while (context.getText().length() > 0) {
                    String string = context.getText().substring(0, 1);
                    switch (string) {
                        case "O": // 当首字母是O时,表达式实例化为音阶
                            expression = new Scale();
                            break;
                        case "T": // 增加对T的判断
                            expression = new Speed();
                            break;
                        case "C":
                        case "D":
                        case "E":
                        case "F":
                        case "G":
                        case "A":
                        case "B":
                        case "P": // 当首字母是CDEFGAB以及休止符P时,则实例化为音符
                            expression = new Note();
                            break;
                    }
                    expression.interpret(context);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    运行结果

    上海滩:
    中速 中音 3 6 3 5 2 3 5 6 高音 1 中音 6 5 1 3 2 
    
  • 相关阅读:
    jackson 枚举 enum json 解析类型 返回数字 或者自定义文字 How To Serialize Enums as JSON Objects with Jackson
    Antd Pro V5 中ProTable 自定义查询参数和返回值
    ES6/Antd 代码阅读记录
    es 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?
    Antd Hooks
    使用.Net Core开发WPF App系列教程(其它 、保存控件内容为图片)
    使用.Net Core开发WPF App系列教程( 三、与.Net Framework的区别)
    使用.Net Core开发WPF App系列教程( 四、WPF中的XAML)
    使用.Net Core开发WPF App系列教程( 二、在Visual Studio 2019中创建.Net Core WPF工程)
    使用.Net Core开发WPF App系列教程( 一、.Net Core和WPF介绍)
  • 原文地址:https://www.cnblogs.com/call-me-devil/p/12045062.html
Copyright © 2011-2022 走看看