【设计模式-4.11】行为型——解释器模式

说明:本文介绍行为型设计模式之一的解释器模式

定义

解释器模式(Interpreter Pattern)指给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。解释器模式是一种按照规定的文法(语法)进行解析的模式,属于行为型设计模式。

(引自《设计模式就该这样学》P385)

自定义脚本

这里介绍《秒懂设计模式》这本书中的举例,假设我们需要定义一个刷怪脚本,脚本语言如下,执行顺序为自上而下。

shell 复制代码
BEGIN					// 脚本开始
MOVE 500,600			// 鼠标移动到屏幕(500, 600)的位置
	BEGIN LOOP 5		// 开始循环5次
		LEFT_CLICK;		// 循环鼠标单击左键
		DELAY 1;		// 每次延迟1秒
	END;				// 循环结束
RIGHt_LEFT;				// 按下鼠标左键
DELAY 7200;				// 延迟7200秒
END:					// 脚本结束

分析一下,这个脚本中包含了一下操作:

  • 移动鼠标

  • 鼠标左键单击(包含按下鼠标左键、松开鼠标左键)

  • 按下鼠标左键

  • 延迟

也就是五个操作:移动鼠标、鼠标左键按下、鼠标左键松开、鼠标左键单击(由鼠标左键按下、鼠标左键松开组合而成)、延迟

另外,还包含了脚本执行的语法顺序,如下:

  • 循环:循环执行某些操作

  • 顺序:脚本的执行是从上到下,顺序执行的


分析完,将上面的脚本语法转为代码语言,如下:

(表达式接口,Expression)

java 复制代码
/**
 * 表达式接口
 */
public interface Expression {

    /**
     * 解释
     */
    void interpret();
}

以下都是解释器,对应上述分析的操作和流程

(移动鼠标,MoveMouse)

java 复制代码
/**
 * 移动鼠标
 */
public class MoveMouse implements Expression {

    /**
     * x坐标
     */
    private int x;

    /**
     * y坐标
     */
    private int y;


    public MoveMouse(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public void interpret() {
        System.out.println("移动鼠标到【" + x + "," + y + "】的位置");
    }
}

(鼠标左键按下,LeftKeyDown)

java 复制代码
/**
 * 鼠标左键按下
 */
public class LeftKeyDown implements Expression {

    @Override
    public void interpret() {
        System.out.println("按下鼠标左键");
    }
}

(鼠标左键松开,LeftKeyUp)

java 复制代码
/**
 * 鼠标左键松开
 */
public class LeftKeyUp implements Expression {

    @Override
    public void interpret() {
        System.out.println("松开鼠标左键");
    }
}

(鼠标左键单击,LeftKeyClick)

java 复制代码
/**
 * 鼠标左键单击
 */
public class LeftKeyClick implements Expression {

    /**
     * 左键按下
     */
    private Expression leftKeyDown;

    /**
     * 左键松开
     */
    private Expression leftKeyUp;

    /**
     * 左键按下
     */
    public LeftKeyClick() {
        this.leftKeyDown = new LeftKeyDown();
        this.leftKeyUp = new LeftKeyUp();
    }

    /**
     * 单击鼠标左键就是先按下鼠标左键, 再松开鼠标左键
     */
    @Override
    public void interpret() {
        leftKeyDown.interpret();
        leftKeyUp.interpret();
    }
}

(延迟,Delay)

java 复制代码
/**
 * 延迟解释器
 */
public class Delay implements Expression {

    /**
     * 延迟秒数
     */
    private int seconds;

    public Delay(int seconds) {
        this.seconds = seconds;
    }

    public int getSeconds() {
        return seconds;
    }

    @Override
    public void interpret() {
        System.out.println("系统延迟:" + seconds + "秒");
        try {
            Thread.sleep(seconds * 1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

(循环,Repetition)

java 复制代码
/**
 * 循环表达式解释器
 */
public class Repetition implements Expression {

    /**
     * 循环次数
     */
    private int loopCount;

    /**
     * 循环体内的表达式
     */
    private Expression loopBodySequence;

    public Repetition(Expression loopBodySequence, int loopCount) {
        this.loopBodySequence = loopBodySequence;
        this.loopCount = loopCount;
    }

    @Override
    public void interpret() {
        while (loopCount > 0) {
            loopBodySequence.interpret();
            loopCount--;
        }
    }
}

(顺序,Sequence)

java 复制代码
import java.util.List;

/**
 * 顺序
 */
public class Sequence implements Expression {

    /**
     * 脚本包含了多个表达式
     */
    private List<Expression> expressions;

    public Sequence(List<Expression> expressions) {
        this.expressions = expressions;
    }

    /**
     * 顺序执行表达式
     */
    @Override
    public void interpret() {
        for (Expression expression : expressions) {
            expression.interpret();
        }
    }
}

客户端使用,将开头的脚本按照规则放入,并解释(为了节约时间,将最后的延迟时间换成10秒)

java 复制代码
import java.util.Arrays;

public class Client {
    public static void main(String[] args) {
        new Sequence(Arrays.asList(
                new MoveMouse(500, 600),
                new Repetition(
                        new Sequence(
                                Arrays.asList(
                                        new LeftKeyClick(), new Delay(1)
                                )
                        ), 5
                ),
                new LeftKeyDown(),
                new Delay(10)
        )).interpret();
    }
}

执行,可见每一个表达式都被成功解释

代码看下来,我认为解释器模式结构上很简单,就是定义一个接口,生成多个解释器实现类,具体的实现逻辑需要根据实际的业务实现。

使用场景

在《设计模式就该这样学》(P386)这本书中,提到解释器模式适用于以下场景:

(1)一些重复出现的问题可以用一种简单的语言进行表示。

(2)一个简单语法需要解释的场景。

例如,项目中需要解析cron表达式,根据解析后的内容去做相应的业务,我想可以考虑使用解释器模式。

总结

本文介绍了行为型设计模式中的访问者模式,参考《设计模式就该这样学》、《秒懂设计模式》两书,自定义脚本是《秒懂设计模式》中的举例。

相关推荐
硅的褶皱3 小时前
对比分析LinkedBlockingQueue和SynchronousQueue
java·并发编程
MoFe13 小时前
【.net core】天地图坐标转换为高德地图坐标(WGS84 坐标转 GCJ02 坐标)
java·前端·.netcore
季鸢3 小时前
Java设计模式之观察者模式详解
java·观察者模式·设计模式
Fanxt_Ja3 小时前
【JVM】三色标记法原理
java·开发语言·jvm·算法
蔡蓝4 小时前
设计模式-迪米特法则
设计模式·log4j·迪米特法则
Mr Aokey4 小时前
Spring MVC参数绑定终极手册:单&多参/对象/集合/JSON/文件上传精讲
java·后端·spring
小马爱记录5 小时前
sentinel规则持久化
java·spring cloud·sentinel
长勺5 小时前
Spring中@Primary注解的作用与使用
java·后端·spring
紫乾20146 小时前
idea json生成实体类
java·json·intellij-idea
wh_xia_jun6 小时前
在 Spring Boot 中使用 JSP
java·前端·spring boot