其实你不懂老板的心--解释器模式

1.1 其实你不懂老板的心

        老板问我最近的工作进展情况如何,有没有什么困难,同事相处如何,等等。其实也就是简单地调查民情吧。""老板私下对某员工大加夸奖时,多半是'最近有更多的任务需要你去完成'的意思。""老板还问我另一个叫梅星的同事的情况。我说他工作也很努力。老板最后评价了句'梅星是个普通员工。"通常老板说某个员工是普通员工,其实他的意思是说,这个员工不够聪明,工作能力不足。"

1.2 解释器模式

        解释器模式(interpreter),给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。[DP]
        "解释器模式需要解决的是,如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题[DP]。比如,我们常常会在字符串中搜索匹配的字符或判断一个字符串是否符合我们规定的格式,此时一般会用什么技术?"
        "是不是正则表达式?"
        "对,非常好,因为这个匹配字符的需求在软件的很多地方都会使用,而且行为之间都非常类似,过去的做法是针对特定的需求,编写特定的函数,比如判断E-mail、匹配电话号码等,与其为每一个特定需求都写一个算法函数,不如使用一种通用的搜索算法来解释执行一个正则表达式,该正则表达式定义了待匹配字符串的集合[DP]。而所谓的解释器模式,正则表达式就是它的一种应用,解释器为正则表达式定义了一个文法,如何表示一个特定的正则表达式,以及如何解释这个正则表达式。"
        "我的理解,是不是像IE、Firefox这些浏览器,其实也是在解释HTML文法,将下载到客户端的HTML标记文本转换成网页格式显示到用户?"
"哈,是可以这么说,不过编写一个浏览器的程序,当然要复杂得多。"
        "下面我们来看看解释器模式实现的结构图和基本代码。"
解释器模式(interpreter)结构图

package code.chapter27.interpreter0;

import java.util.ArrayList;

public class Test {

    public static void main(String[] args) {

        System.out.println("**********************************************");       
        System.out.println("《大话设计模式》代码样例");
        System.out.println(); 

        Context context = new Context();
        ArrayList<AbstractExpression> list = new ArrayList<AbstractExpression>();
        list.add(new TerminalExpression());
        list.add(new NonterminalExpression());
        list.add(new TerminalExpression());
        list.add(new TerminalExpression());

        for (AbstractExpression exp : list) {
            exp.interpret(context);
        }

        System.out.println();
        System.out.println("**********************************************");
    }
}

//抽象表达式类
abstract class AbstractExpression {
    //解释操作
    public abstract void interpret(Context context);
}

//终结符表达式
class TerminalExpression extends AbstractExpression {
    public void interpret(Context context) {
        System.out.println("终端解释器");
    }
}

//非终结符表达式
class NonterminalExpression extends AbstractExpression {
    public void interpret(Context context) {
        System.out.println("非终端解释器");
    }
}

class Context {
    private String input;
    public String getInput(){
        return this.input;
    }
    public void setInput(String value){
        this.input = value;
    }
    
    private String output;
    public String getOutput(){
        return this.output;
    }
    public void setOutput(String value){
        this.output = value;
    }
}


AbstractExpression(抽象表达式),声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。
TerminalExpression(终结符表达式),实现与文法中的终结符相关联的解释操作。实现抽象表达式中所要求的接口,主要是一个interpret()方法。文法中每一个终结符都有一个具体终结表达式与之相对应。
NonterminalExpression(非终结符表达式),为文法中的非终结符实现解释操作。对文法中每一条规则R1、R2、…、Rn都需要一个具体的非终结符表达式类。通过实现抽象表达式的interpret()方法实现解释操作。解释操作以递归方式调用上面所提到的代表R1、R2、…、Rn中各个符号的实例变量。
Context,包含解释器之外的一些全局信息。
客户端代码,构建表示该文法定义的语言中一个特定的句子的抽象语法树。调用解释操作。

1.3 解释器模式的好处

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

        "解释器模式有什么好处呢?"
        "用了解释器模式,就意味着可以很容易地改变和扩展文法,因为该模式使用类来表示文法规则,你可使用继承来改变或扩展该文法。也比较容易实现文法,因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写[DP]。"
        "除了像正则表达式、浏览器等应用,解释器模式还能用在什么地方呢?"
        "只要是可以用语言来描述的,都可以是应用呀。比如针对机器人,如果为了让它走段路还需要去电脑面前调用向前走、左转、右转的方法,那也就太傻了吧。当然应该直接是对它说,'哥们儿,向前走10步,然后左转90度,再向前走5步。'"
        "哈,机器人听得懂'哥们儿'是什么意思吗?"
        "这就看你写的解释器够不够用了,如果你增加这个'哥们儿'的文法,它就听得懂呀。说白了,解释器模式就是将这样的一句话,转变成实际的命令程序执行而已。而不用解释器模式本来也可以分析,但通过继承抽象表达式的方式,由于依赖倒转原则,使得对文法的扩展和维护都带来了方便。"
        "哈,难道说,Java、C#这些高级语言都是用解释器模式的方式开发的?"
        "当然不是那么简单了,解释器模式也有不足的,解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。建议当文法非常复杂时,使用其他的技术如语法分析程序或编译器生成器来处理[DP]"。
        "哦,原来还有语法分析器、编译器生成器这样的东西。"

1.4 音乐解释器

        "现在我定义一套规则,和QB的有点类似,但为了简便起见,我做了改动,你就按我定义的规则来编程。我的规则是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。"

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

1.5 音乐解释器实现

代码结构图

package code.chapter27.interpreter1;

import java.util.ArrayList;

public class Test {

    public static void main(String[] args) {

        System.out.println("**********************************************");       
        System.out.println("《大话设计模式》代码样例");
        System.out.println(); 

        PlayContext context = new PlayContext();
        //音乐-上海滩
        System.out.println("音乐-上海滩:");
        context.setPlayText("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 ");

        Expression expression=null;    
        //System.out.println(context.getPlayText().length());
        while (context.getPlayText().length() > 0) {
            String str = context.getPlayText().substring(0, 1);
            switch (str) {
                case "O":
                    expression = new Scale();
                    break;
                case "C":
                case "D":
                case "E":
                case "F":
                case "G":
                case "A":
                case "B":
                case "P":
                    expression = new Note();
                    break;

            }
            expression.interpret(context);
        }
        
        System.out.println();
        
        System.out.println();
        System.out.println("**********************************************");
    }
}

//演奏内容
class PlayContext {
    private String playText;
    public String getPlayText(){
        return this.playText;
    }
    public void setPlayText(String value){
        this.playText = value;
    }
}

//抽象表达式类
abstract class Expression {
    
    public void interpret(PlayContext context) {
      if (context.getPlayText().length() == 0) {
          return;
      }
      else {
        String playKey = context.getPlayText().substring(0, 1);
          
        context.setPlayText(context.getPlayText().substring(2));

        double playValue = Double.parseDouble(context.getPlayText().substring(0,context.getPlayText().indexOf(" ")));
        context.setPlayText(context.getPlayText().substring(context.getPlayText().indexOf(" ") + 1));

        this.excute(playKey, playValue);
      }
    }
    //抽象方法“执行”,不同的文法子类,有不同的执行处理
    public abstract void excute(String key, double value);
}

//音符类
class Note extends Expression {
    public void excute(String key, double value) {
        String note = "";
        switch (key) {
            case "C":
                note = "1";
                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+" ");
    }
}

//音阶类
class Scale extends Expression {
    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+" ");
    }
}

演奏内容类(context):
表达式类(AbstractExpression):
音符类(TerminalExpression):
音阶类(TerminalExpression):

        "现在我需要增加一个文法,就是演奏速度,要求是T代表速度,以毫秒为单位,'T 1000'表示每节拍一秒,'T 500'表示每节拍半秒。你如何做?"

"学了设计模式这么久,这点感觉难道还没有,首先加一个表达式的子类叫音速。然后再在客户端的分支判断中增加一个case分支就可以了。"

音速类:

package code.chapter27.interpreter2;

import java.util.ArrayList;

public class Test {

    public static void main(String[] args) {

        System.out.println("**********************************************");       
        System.out.println("《大话设计模式》代码样例");
        System.out.println(); 

        PlayContext context = new PlayContext();
        //音乐-上海滩
        System.out.println("音乐-上海滩:");
        context.setPlayText("T 500 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 ");

        Expression expression=null;    
        while (context.getPlayText().length() > 0) {
            String str = context.getPlayText().substring(0, 1);

            switch (str) {
                case "O":
                    expression = new Scale();
                    break;
                case "C":
                case "D":
                case "E":
                case "F":
                case "G":
                case "A":
                case "B":
                case "P":
                    expression = new Note();
                    break;
                case "T":
                    expression = new Speed();
                    break;

            }
            expression.interpret(context);
        }
        
        System.out.println();
        System.out.println();
        System.out.println("**********************************************");
    }
}

//演奏内容
class PlayContext {
    private String playText;
    public String getPlayText(){
        return this.playText;
    }
    public void setPlayText(String value){
        this.playText = value;
    }
}

//抽象表达式类
abstract class Expression {
    //解释器
    public void interpret(PlayContext context)
    {
        if (context.getPlayText().length() == 0) {
            return;
        }
        else {
            String playKey = context.getPlayText().substring(0, 1);
            //System.out.println("playKey:"+playKey);
        
            context.setPlayText(context.getPlayText().substring(2));

            double playValue = Double.parseDouble(context.getPlayText().substring(0, context.getPlayText().indexOf(" ")));
            context.setPlayText(context.getPlayText().substring(context.getPlayText().indexOf(" ") + 1));

            this.excute(playKey, playValue);

        }
    }
    //执行
    public abstract void excute(String key, double value);
}

//音符类
class Note extends Expression {
    public void excute(String key, double value) {
        String note = "";
        switch (key) {
            case "C":
                note = "1";
                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+" ");
    }
}

//音阶类
class Scale extends Expression {
    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+" ");
    }
}

//音速类
class Speed extends Expression {
    public void excute(String key, double value) {
        String speed;
        if (value < 500)
            speed = "快速";
        else if (value >= 1000)
            speed = "慢速";
        else
            speed = "中速";

        System.out.print(speed+" ");
    }
}

        "在增加一个文法时,你除了扩展一个类外,还是改动了客户端。"
        "哈,这不就是实例化的问题吗,只要在客户端的switch那里应用简单工厂加反射就可做到不改动客户端了。"
        "说得好,看来你的确是学明白了,在这里是讲解释器模式,也就不那么追究了,只要知道可以这样重构程序就行。其实这个例子是不能代表解释器模式的全貌的,因为它只有终结符表达式,而没有非终结符表达式的子类,因为如果想真正理解解释器模式,还需要去研究其他的例子。另外这是个控制台的程序,如果我给你钢琴所有按键的声音文件,MP3格式,你可以利用Media Player控件,写出真实的音乐语言解释器程序吗?"
        "你的意思是,只要我按照简谱编好这样的语句,就可以让电脑模拟钢琴弹奏出来?"

1.6 料事如神

        "第一,梅星离职了,说是他辞职,可听说实际上是公司叫他走人的。"
"唉,所以说呀,好好学习,加强自己的市场竞争力还是非常重要的。"
        "第二,就是梅星的工作全部转给我做了,这样我的工作量大大提高,一个人干了两个人的活。"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值