Java解释器模式

我们学习计算机程序语言,需要符合语法,是因为编译器会帮我们翻译我们的程序代码,那么Java的解释器模式就是一种解释文法的设计模式。

解释器模式定义:

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

从上面的定义我们知道我们知道,首先就是要有一种文法,文法就是所谓的语法,像我们的汉语一样,一般请况之下我们要有主谓宾结构,比如:我 打 你,主语是: 我 ,谓语是一个动词: 打, 宾语是:你。

解释器无非就是解释符合文法的一个句子:

比如 “left move 10” 

假设这是符合文法的一个句子,我们翻译成汉语就是 “向下 移动 10”

好了,为了更好的体现解释器设计模式,我们来用代码作为示例,翻译一下: left move 10 and up run 100;

代码:

首先我们要定义抽象的解释器:

/**
 * 抽象的文法解释器
 */
public abstract class AbstractNode {

    /**
     * 解释方法
     * @return 解释后的结果
     */
    public abstract String interpret();

}

left 是一个方向词,那么我们也为他实现一个具体的文法解释器:

/**
 * 方向文法的解释器
 */
public class DirectionNode extends AbstractNode {

    private String direction; // 具体的方向

    public DirectionNode(String direction) {
        this.direction = direction;
    }

    /**
     * 对向下左右的文法进行解释
     * @return 文法解释后的结果
     */
    @Override
    public String interpret() {
        if(direction.equalsIgnoreCase("up")) {
            return "向上";
        }else if(direction.equalsIgnoreCase("down")) {
            return "向下";
        }else if(direction.equalsIgnoreCase("left")) {
            return "向左";
        }else if(direction.equalsIgnoreCase("right")) {
            return "向右";
        }else {
            return "无效指令";
        }
    }
}

move 是一个动词,我们也要为动词实现一个解释器:

/**
 * 动作的文法
 */
public class ActionNode extends AbstractNode {

    private String action; // 具体的动作,对具体的动作进行解释

    public ActionNode(String action) {
        this.action = action;
    }

    /**
     * 如果动作是move 就返回移动 ,如果是run 就返回奔跑,否则就是无效的文法指令
     * @return 解释后的结果
     */
    @Override
    public String interpret() {
        if(action.equalsIgnoreCase("move")) {
            return "移动";
        }else if(action.equalsIgnoreCase("run")) {
            return "奔跑";
        }else {
            return "无效指令";
        }
    }

}

10 是距离 ,接下来是一个距离的解释器:

/**
 * 距离文法解释
 */
public class DistanceNode extends AbstractNode {

    private String distance; // 具体的距离

    public DistanceNode(String distance) {
        this.distance = distance;
    }

    /**
     * 解释文法中的距离
     * @return 解释后的结果
     */
    @Override
    public String interpret() {
        return distance + "米";
    }

}

既然已经有 left move 10 ,很显然这是一个完整的短句了,那么我们就要有一个短句的解释器:

/**
 * 句式解释器
 */
public class SentenceNode extends AbstractNode {

    private AbstractNode direction; // 句式中的方向
    private AbstractNode action; // 句式中的动作
    private AbstractNode distance; // 句式中的距离

    public SentenceNode(AbstractNode direction,AbstractNode action,AbstractNode distance) {
        this.direction = direction;
        this.action = action;
        this.distance = distance;
    }

    /**
     * 解释句式,句式一定要是 方向 + 动作 + 距离 如 : up move 10,如果不是就会出错
     * @return 解释后的结果
     */
    @Override
    public String interpret() {
        return this.direction.interpret() + this.action.interpret() + this.distance.interpret();
    }
}

上面的短句解释器维护了 方向 动作 距离三种解释器来完成解释一个短句的语法

然后一个短句完成了,and 这一个连接词 连接了两个短句,那么我们要用一个and解释器来解释整个一个长句

/**
 * and 连接词文法解释器
 */
public class AndNode extends AbstractNode {

    private AbstractNode left; //左表达式
    private AbstractNode right;	//右表达式


    AndNode(AbstractNode left,AbstractNode right){
        this.left = left;
        this.right = right;
    }

    /**
     * 连接左右文法的解释
     * @return 解释后的结果
     */
    @Override
    public String interpret() {
        return this.left.interpret() +  " 再 " + this.right.interpret();
    }
}

这样,所有零件工具的解释器都有了

但是我们没有解释整个句子的总处理类,我们要定义怎么解释句式,就需要有一个句式处理器:

/**
 * 句子解释处理类,也是上下文类
 */
public class InstructionHandler {

    private AbstractNode node;

    public void Handle(String instruction) {
        //用栈来保存语法树。
        AbstractNode left;
        AbstractNode right;
        AbstractNode direction;
        AbstractNode action;
        AbstractNode distance;

        AbstractNode andNode;
        Stack<AbstractNode> stack = new Stack<>();
        //用空格分隔指令
        String[] word = instruction.split(" ");
        //循环
        for(int i = 0; i < word.length; i++) {
            if(word[i].equalsIgnoreCase("and")) { // 如果遇到连接词and,先将左解释器弹出,然后解释右半部,最后左右合成一个连接解释器
                left = stack.pop();
                String wordl = word[++i];
                direction = new DirectionNode(wordl);
                String word2 = word[++i];
                action = new ActionNode(word2);
                String word3 = word[++i];
                distance = new DistanceNode(word3);
                right = new SentenceNode(direction, action, distance);
                andNode = new AndNode(left,right);
                stack.push(andNode);
            }else { // 如果没有遇到连接词and ,解释 方向 + 动作 + 距离,合成一个 短句解释器
                String word1 = word[i];
                direction = new DirectionNode(word1);
                String word2 = word[++i];
                action = new ActionNode(word2);
                String word3 = word[++i];
                distance = new DistanceNode(word3);
                left= new SentenceNode(direction, action, distance);
                stack.push(left);
            }
        }

        this.node = stack.pop();
    }

    public String output() {
        String result = node.interpret();
        return result;
    }

}

好了,接下来就是要测试了,上测试代码:

public class InterpreterTest {

    public static void main(String[] args) {
        String instruction ="down run 10 and left move 20 and up run 500";
        InstructionHandler iHandler = new InstructionHandler();
        iHandler.Handle(instruction);
        String outString ;
        outString =iHandler.output();
        System.out.println(outString);
    }
}

测试结果:

向下奔跑10米 再 向左移动20米 再 向上奔跑500米

上面我们使用翻译一个句子实例实现了一个Java解释器模式。想必大家也差不多能动解释器模式了

解释器模式的总结:

优点:

1. 易于改变和扩展文法,由于在解释器模式中使用类表示语言的文法规则,因此可以通过继承等机制来改变和扩张文法。
2. 每一条文法规则都可以表示为一个类,因此可以方便地实现每一个简单的语言。
3. 实现文法比较容易。


缺点:

1. 对于复杂文法难以维护,再解释模式下每一条规则至少定义一个类,因此如果太复杂无法处理。
2. 执行效率低,由于大量使用了循环和递归。因此再解释很复杂的句子时速度很慢


使用场景:

1. 可以将一个需要解释执行的语言中的句子表示为一个抽象树。
2. 一些重复出现的问题可以用简单的语言进行表达。
3. 执行效率不是关键问题。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值