Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >设计模式 | 行为型 | 解释器模式

设计模式 | 行为型 | 解释器模式

作者头像
被水淹没
发布于 2023-02-25 11:19:41
发布于 2023-02-25 11:19:41
41001
代码可运行
举报
文章被收录于专栏:迈向架构师迈向架构师
运行总次数:1
代码可运行

解释器模式(Interpreter)

介绍

解释器模式是一种行为设计模式。

解释器模式为某个语言定义它的语法表示,并定义一个解释器用来处理这个语法。

适用场景

  • 构建一个“语言”解释器。
  • 实现编译器、规则引擎、正则表达式等功能。
  • 将一些重复出现的问题用简单的语法来进行表达。
  • 将一个需要解释执行的语言中的句子表示为一个抽象语法树。
  • ...

优缺点

优点:

  • 开闭原则。
  • 单一职责原则。

缺点:

  • 可以利用的场景比较少。
  • 如果文法复杂的话可能较难维护。

与其他模式的关系

  • ...

实现方式

解释器模式代码实现的核心思想就是将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。

解释器模式的代码实现比较灵活没有固定的模板,在我的示例中的做法大体可以拆分为:

  1. 定义表达式接口。
  2. 实现表达式具体类。
  3. 实现解释器上下文类,作为“启动器”,负责调用表达式进行。
  4. 客户端通过解释器上下文进行调用。

举例

我这里的示例代码主要实现一个类似 poi-tl 模板引擎的功能。

什么是 poi-tl 呢?

poi-tl是一个基于 Apache POI 的 Word 模板引擎,给它一个 word 模板文件以及数据内容,即可生成一个基于模板文件更新的 word 文件。

举个使用的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 新建一个模板文件:template.docx ,并且在里面添加内容:{{title}}
// poi-tl 的所有的标签都是以 {{ 开头,以 }} 结尾
{{title}}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
XWPFTemplate template=XWPFTemplate.compile("template.docx").render(
    new HashMap<String, Object>(){{
        put("title","Hi, poi-tl Word模板引擎");
}});
template.writeAndClose(new FileOutputStream("output.docx")); 
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 输出的 output.docx 文件内容:
Hi,poi-tl Word模板引擎

示例

模仿 poi-tl 功能的实现:

表达式抽象类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface Expression {
    String interpret(String sourceStr, Map<String, String> data);
}

表达式具体实现类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 表达式实现:文本表达式
 */
public class ExpressionStr implements Expression {

    private static String prefix = "{{";
    private static String suffix = "}}";

    @Override
    public String interpret(String sourceStr, Map<String, String> data) {

        for (Map.Entry<String, String> entry : data.entrySet()) {
            String targerStr = prefix + entry.getKey() + suffix;
            sourceStr = StrUtil.replace(sourceStr, targerStr, entry.getValue());
        }
        return sourceStr;
    }
}

/**
 * 表达式实现:表达式实现:图片表达式
 */
public class ExpressionPic implements Expression {

    private static String prefix = "{{@";
    private static String suffix = "}}";

    @Override
    public String interpret(String sourceStr, Map<String, String> data) {

        for (Map.Entry<String, String> entry : data.entrySet()) {
            String targerStr = prefix + entry.getKey() + suffix;
            String imageStr = "![" + entry.getKey() + "](assets/" + entry.getValue() + "]";
            sourceStr = StrUtil.replace(sourceStr, targerStr, imageStr);
        }
        return sourceStr;
    }
}

解释器上下文:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Interpreter {

    // 源文本
    private String sourceStr;
    // 表达式
    private Map<Expression, Map<String, String>> expressions = new HashMap<>();

    public Interpreter(String sourceStr) {
        this.sourceStr = sourceStr;
    }

    /**
     * 添加表达式
     */
    public void addExpression(Expression expression, Map<String, String> data) {
        expressions.put(expression, data);
    }

    /**
     * 执行解释
     */
    public String interpreter() {

        expressions.forEach((expression, data) -> {
            sourceStr = expression.interpret(sourceStr, data);
        });

        return sourceStr;
    }
}

测试代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class InterpreterTest {
    @Test
    public void test() {

        String content = "这是一篇文章,文章标题为{{title}},内容主要是讲{{subject}}," +
                "目前评分{{score}}分,附带题图{{@picture}},内容图{{@picture2}}";

        Interpreter interpreter = new Interpreter(content);

        // 套用文字模板进行渲染
        ExpressionStr expressionStr = new ExpressionStr();
        interpreter.addExpression(expressionStr, new HashMap() {{
            put("title", "解释器模式");
            put("subject", "模仿poi-tl的渲染方式展示解释器模式");
            put("score", "3.7");
        }});

        // 套用图片模板进行渲染
        ExpressionPic expressionPic = new ExpressionPic();
        interpreter.addExpression(expressionPic, new HashMap() {{
            put("picture", "/picture/1.jpg");
            put("picture2", "/picture/2.jpg");
        }});

        // 进行解释
        String result = interpreter.interpreter();
        Assertions.assertEquals("这是一篇文章,文章标题为解释器模式," +
                        "内容主要是讲模仿poi-tl的渲染方式展示解释器模式," +
                        "目前评分3.7分,附带题图![picture](assets//picture/1.jpg]," +
                        "内容图![picture2](assets//picture/2.jpg]"
                , result);
    }
}

实例

Mybatis SqlNode

利用解释器模式来解析动态 SQL

表达式抽象类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface SqlNode {
    /**
     * 将解析出来的值应用到context中
     */
    boolean apply(DynamicContext context);
}

解释器上下文:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class RawSqlSource implements SqlSource {

    private final SqlSource sqlSource;

    public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
        this(configuration, getSql(configuration, rootSqlNode), parameterType);
    }

    public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
        SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
        Class<?> clazz = parameterType == null ? Object.class : parameterType;
        sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
    }

    /**
     * 通过遍历所有的SqlNode,获取sql
     */
    private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
        DynamicContext context = new DynamicContext(configuration, null);
        rootSqlNode.apply(context);
        return context.getSql();
    }

    @Override
    public BoundSql getBoundSql(Object parameterObject) {
        return sqlSource.getBoundSql(parameterObject);
    }

}

SqlNode的具体实现,

例如在 scripting/xmltags 文件夹下的 MixedSqlNode、StaticTextSqlNode、IfSqlNode 等:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 保存一个 CRUD 节点下所含有的所有 SqlNode 集合,方便统一管理以及解析
 */
public class MixedSqlNode implements SqlNode {
    // 记录sql节点中的所有SQL片段
    private final List<SqlNode> contents;

    public MixedSqlNode(List<SqlNode> contents) {
        this.contents = contents;
    }

    @Override
    public boolean apply(DynamicContext context) {
        // 循环调用contents集合中的所有SqlNode对象的apply方法
        contents.forEach(node -> node.apply(context));
        return true;
    }
}

/**
 * 最简单的SqlNode,功能仅仅就是将自身记录的text拼接到context上下文中
 */
public class StaticTextSqlNode implements SqlNode {
    private final String text;

    public StaticTextSqlNode(String text) {
        this.text = text;
    }

    @Override
    public boolean apply(DynamicContext context) {
        context.appendSql(text);
        return true;
    }
}

/**
 * 最常用的 if 标签,满足条件则合并到 contents 中,不满足则抛弃
 */
public class IfSqlNode implements SqlNode {
    private final ExpressionEvaluator evaluator;
    private final String test;
    private final SqlNode contents;

    public IfSqlNode(SqlNode contents, String test) {
        this.test = test;
        this.contents = contents;
        this.evaluator = new ExpressionEvaluator();
    }

    @Override
    public boolean apply(DynamicContext context) {
        if (evaluator.evaluateBoolean(test, context.getBindings())) {
            contents.apply(context);
            return true;
        }
        return false;
    }
}

// ... 其他 SqlNode 的实现略

以上代码与文章会同步到 github 仓库:

/chenbihao/Design-Patterns

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-12-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 迈向架构师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
最强大的 CSS 布局 —— Grid 布局
Grid 布局即网格布局,是一种新的 CSS 布局模型,比较擅长将一个页面划分为几个主要区域,以及定义这些区域的大小、位置、层次等关系。号称是最强大的的 CSS 布局方案,是目前唯一一种 CSS 二维布局。利用 Grid 布局,我们可以轻松实现类似下图布局,演示地址[1]
GopalFeng
2020/09/24
16.3K0
最强大的 CSS 布局 —— Grid 布局
Grid网格布局入门
它将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局。以前,只能通过复杂的 CSS 框架达到的效果,现在浏览器内置了。
javascript.shop
2019/09/04
2.3K1
Grid网格布局入门
【CSS】最强大的布局之grid布局精讲
轮播图swiper框架的基本使用 【Transform3D】转换详解(看完就会) 【css动画】移动的小车
坚毅的小解同志的前端社区
2022/11/28
3.1K0
【CSS】最强大的布局之grid布局精讲
【图片版】CSS网格布局(Grid)完全教程
CSS网格布局(Grid)是一套二维的页面布局系统,它的出现将完全颠覆页面布局的传统方式。传统的CSS页面布局 一直不够理想。包括table布局、浮动、定位及内联块等方式,从本质上都是Hack的方式,并且遗漏了一些重要的功能(比如:垂直居中)。Flexbox的出现部分解决了上述问题,但Flex布局是为了解决简单的一维布局,适用于页面局部布局。而Grid天然就是为了解决复杂的二维布局而出现的,适用页面的整体布局。在实际工作中,Grid和Flexbox不但不矛盾,而且还能很好的结合使用。做为WEB程序员,我们在页面布局问题上都付出过努力,也将不断探索新的方案。而Grid是第一个专门为布局问题而生的CSS模块,我们有理由对Grid充满期待。
毛瑞
2018/05/02
5.3K0
【图片版】CSS网格布局(Grid)完全教程
[译]二维布局:Grid Layout
CSS Grid Layout 是 CSS 中最强大的布局系统。不像 flexbox 那样的一维系统,它是一个二维系统,可以同时处理列和行。您可以通过将规则应用到父元素来使用 Grid Layout。
码农小余
2022/06/16
4.7K0
[译]二维布局:Grid Layout
CSS(七)
Grid 布局是 CSS 中最强大的布局系统。 Grid 布局是一个二维布局系统,意味着它可以处理列和行,不像 Flexbox 主要是一维布局系统。通过将 CSS 规则应用于父元素(称为 grid container)和子元素(称为 grid items),我们就可以使用网格布局。
1ess
2021/10/29
5430
CSS(七)
图解CSS布局(一)- Grid布局
Grid 布局是将容器划分成"行"和"列",产生单元格,然后指定"项目所在"的单元格,可以看作是二维布局,也是唯一的二维布局方案,利用grid布局可以很轻松的实现很多的网页布局
小丞同学
2021/08/16
2.1K0
万字总结 CSS 布局
本文总结了主流的几种 CSS 的布局方法,无论你是一个想要学习 CSS 布局的新手,还是一个比较有经验但想要进一步巩固与了解最新CSS布局知识的前端开发者,这篇指南都能帮你全面了解如今CSS布局发展的现状。
用户8921923
2022/10/24
6.1K0
万字总结 CSS 布局
grid网格布局
​ 距今来看已经十多年了,确确实实占据了前端布局中很大的地位,相比大家对flex都很熟悉,所以今天我们的主角就是-------> Grid
Snine
2022/02/11
2.1K0
grid网格布局
Web前端学习 第2章 网页重构16 grid布局
grid容器的水平区域成为行(row),垂直区域成为列(column),行与列之间的较差与是单元格(cell),划分网格的线成为网格线(gird line),了解了这些基本概念之后,就可以开始用相应的css属性设置grid容器中的项目了。
学习猿地
2020/06/15
1K0
Web前端学习 第2章 网页重构16 grid布局
Grid布局详解:打造完美的网页布局
随着Web技术的不断发展,网页布局也在不断地改进和完善,其中Grid布局是最受欢迎的一种布局方式。它是一种基于网格线的布局方式,可以轻松地实现复杂的网页布局,而且还具有很强的可读性和可维护性。
Front_Yue
2023/12/22
1.7K0
Grid布局详解:打造完美的网页布局
CSS 中的 Grid 布局 完全指南
Grid 是一个基于二维网格布局的系统,有了它我们可以非常方便的实现过去很复杂布局,无需再使用float, inline-block, position 这些本质上是 hack 的方法。
羽月
2022/10/08
4.2K0
CSS 中的 Grid 布局 完全指南
10分钟理解CSS3 Grid
上一篇文章我们介绍了 css3 flexbox,今天我们再来说说css3的另外一个强大的功能:Grid。 Grid做前端的同学应该都很熟悉了,翻译成中文为“栅格”,用过bootstrap、semantic ui、ant design的同学肯定都了解grid layout(删格布局),以往css框架中的grid布局一般是通过float和百分比的宽度实现的,这种实现有几种缺点:
MudOnTire
2019/05/26
7910
CSS Flexbox与Grid:构建响应式布局的艺术
开启Flex布局模式。将一个元素设置为Flex容器,其直接子元素将成为Flex项目。
天涯学馆
2024/05/22
5370
CSS Flexbox与Grid:构建响应式布局的艺术
grid布局—让css变得更简单
通过将属性display的值设为grid,使 HTML 元素变为网格容器。在 CSS 网格中,父元素称为容器(container),它的子元素称为项(items)。
_kyle
2020/08/24
5.7K0
grid布局—让css变得更简单
grid 布局的使用
grid 布局的使用    css 网格布局,是一种二维布局系统。    浏览器支持情况:老旧浏览器不支持,    概念: 网格容器。元素应用dispalay:grid,它是所有网格项的父元素。       <div class="container">          <div class="item item-1"></div>         <div class="item item-2"></div>         <div class="item item-3"></div>       
用户1197315
2018/01/22
1.7K0
[CSS] 栅格化布局
栅格化布局帮助你更容易构建复杂的网页设计。它会将HTML元素转换为网格的容器(有行有列)。你可以在网格里面添加你想要的子元素。
Jimmy_is_jimmy
2020/10/29
1.3K0
CSS Grid 新手入门
另外,下面一段话摘自A Complete Guide to Grid,对于CSS Grid会有更加清楚地释义
糊糊糊糊糊了
2018/09/28
2.3K0
CSS Grid 新手入门
grid布局了解一下
首先,看一张图,了解一下容器和项目(不难理解,容器就是包在外层的元素,项目就是内部的元素)
用户4793865
2023/01/12
5200
grid布局了解一下
GRID布局
目前CSS布局方案中,网格布局可以算得上是最强大的布局方案了。它可以将网页分为一个个网格,然后利用这些网格组合做出各种各样的布局。Grid布局与Flex布局有一定的相似性,都可以指定容器内部多个成员的位置。不同之处在于,Flex布局是轴线布局,只能指定成员针对轴线的位置,可以看作是一维布局。Grid布局则是将容器划分成行和列,产生单元格,然后指定成员所在的单元格,可以看作是二维布局。
WindRunnerMax
2020/08/27
1.5K0
相关推荐
最强大的 CSS 布局 —— Grid 布局
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验