文档章节

行为型模式:解释器模式

LieBrother
 LieBrother
发布于 04/15 08:07
字数 1858
阅读 137
收藏 2

原文首发: 行为型模式:解释器模式

鲨鱼

十一大行为型模式之十:解释器模式。

简介

姓名 :解释器模式

英文名 :Interpreter Pattern

价值观 :不懂解释到你懂

个人介绍

Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language. 给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。 (来自《设计模式之禅》)

你要的故事

解释器顾名思义就是对 2 个不同的表达方式进行转换,让本来不懂的内容解释成看得懂的。比如翻译官就是解释器,把英文翻译成中文,让我们明白外国人说什么。咱们工作中也有很多类似的场景,开发系统避免不了使用数据库,数据库有特定的语法,我们称为 SQL (Structured Query Language),而我们系统开发语言和 SQL 的语法不一样,这中间就需要做一层转换,像把 Java 语言中的 userDao.save(user) 变成 insert into user (name,age) values ('小明', 18),这一层转换也可以称为解释器。很多框架实现了这个功能,比如 Hibernate,我们称这些框架为 ORM

今天,我们就来简单的实现 SQL 拼接解释器,通过参数组装成我们要的 SQL 语句。好多开发同学都吐槽工作天天在 CRUD,也就是只干增删改查的活,对于 SQL 我们经常用的也就是这 4 种语法:insert 语句、delete 语句、update 语句、select 语句。这 4 种语法各有不同,也即需要不同的解释器去解析。利用今天要讲的解释器模式,我们来实现一番。

解释器模式中,会有一个上下文类,这个类用于给解释器传递参数。这里我们 SQL 解释器需要的参数分别是

  1. tableName :数据库名
  2. params :修改时更新后的数据
  3. wheres :where 语句后的条件
class Context {
    private String tableName;
    private Map<String, Object> params = new HashMap<>();
    private Map<String, Object> wheres = new HashMap<>();

    public String getTableName() {
        return tableName;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public Map<String, Object> getParams() {
        return params;
    }

    public void setParams(Map<String, Object> params) {
        this.params = params;
    }

    public Map<String, Object> getWheres() {
        return wheres;
    }

    public void setWheres(Map<String, Object> wheres) {
        this.wheres = wheres;
    }
}

解释器主角来了,定义 SQL 解释器抽象类,它有一个抽象方法 interpret,通过这个方法来把 context 中的参数解释成对应的 SQL 语句。

/**
 * SQL 解释器
 */
abstract class SQLExpression {

    public abstract String interpret(Context context);

}

我们上面说了 SQL 语句用的比较多的就是 4 种,每一种其实就是一个解释器,因为语法不一样,解释的逻辑也就不一样,我们就利用 SQLExpression 解释器抽象类,来实现 4 个具体的 SQL 解释器,分别如下:

Insert SQL 解释器代码实现:

/**
 * Insert SQL 解释器
 */
class InsertSQLExpression extends SQLExpression {

    @Override
    public String interpret(Context context) {
        StringBuilder insert = new StringBuilder();
        insert.append("insert into ")
                .append(context.getTableName());

        // 解析 key value
        StringBuilder keys = new StringBuilder();
        StringBuilder values = new StringBuilder();
        keys.append("(");
        values.append("(");
        for (String key : context.getParams().keySet()) {
            keys.append(key).append(",");
            values.append("'").append(context.getParams().get(key)).append("',");
        }
        keys = keys.replace(keys.length() - 1, keys.length(), ")");
        values = values.replace(values.length() - 1, values.length(), ")");

        // 拼接 keys values
        insert.append(keys)
                .append(" values ")
                .append(values);

        System.out.println("Insert SQL : " + insert.toString());
        return insert.toString();
    }
}

Update SQL 解释器代码实现:

/**
 * Update SQL 解释器
 */
class UpdateSQLExpression extends SQLExpression {

    @Override
    public String interpret(Context context) {
        StringBuilder update = new StringBuilder();
        update.append("update ")
                .append(context.getTableName())
                .append(" set ");

        StringBuilder values = new StringBuilder();
        for (String key : context.getParams().keySet()) {
            values.append(key)
                    .append(" = '")
                    .append(context.getParams().get(key))
                    .append("',");
        }

        StringBuilder wheres = new StringBuilder();
        wheres.append(" 1 = 1 ");
        for (String key : context.getWheres().keySet()) {
            wheres.append(" and ")
                    .append(key)
                    .append(" = '")
                    .append(context.getWheres().get(key))
                    .append("'");
        }

        update.append(values.substring(0, values.length() - 1))
                .append(" where ")
                .append(wheres);

        System.out.println("Update SQL : " + update.toString());
        return update.toString();
    }
}

Select SQL 解释器代码实现:

/**
 * Select SQL 解释器
 */
class SelectSQLExpression extends SQLExpression {

    @Override
    public String interpret(Context context) {
        StringBuilder select = new StringBuilder();
        select.append("select * from ")
                .append(context.getTableName())
                .append(" where ")
                .append(" 1 = 1 ");
        for (String key : context.getWheres().keySet()) {
            select.append(" and ")
                    .append(key)
                    .append(" = '")
                    .append(context.getWheres().get(key))
                    .append("'");
        }
        System.out.println("Select SQL : " + select.toString());
        return select.toString();
    }
}

Delete SQL 解释器代码实现

/**
 * Delete SQL 解释器
 */
class DeleteSQLExpression extends SQLExpression {

    @Override
    public String interpret(Context context) {
        StringBuilder delete = new StringBuilder();
        delete.append("delete from ")
                .append(context.getTableName())
                .append(" where ")
                .append(" 1 = 1");
        for (String key : context.getWheres().keySet()) {
            delete.append(" and ")
                    .append(key)
                    .append(" = '")
                    .append(context.getWheres().get(key))
                    .append("'");
        }
        System.out.println("Delete SQL : " + delete.toString());

        return delete.toString();
    }
}

测试代码

public class InterpreterTest {
    public static void main(String[] args) {
        Context context = new Context();
        context.setTableName("user");

        // Insert SQL
        Map<String, Object> params = new HashMap<>();
        params.put("name", "小明");
        params.put("job", "Java 工程师");
        context.setParams(params);
        SQLExpression sqlExpression = new InsertSQLExpression();
        String sql = sqlExpression.interpret(context);

        // Delete SQL
        Map<String, Object> wheres = new HashMap<>();
        wheres.put("name", "小明");
        context.setParams(null);
        context.setWheres(wheres);
        sqlExpression = new DeleteSQLExpression();
        sql = sqlExpression.interpret(context);

        // Update SQL
        params = new HashMap<>();
        params.put("job", "Java 高级工程师");
        wheres = new HashMap<>();
        wheres.put("name", "小明");
        context.setParams(params);
        context.setWheres(wheres);
        sqlExpression = new UpdateSQLExpression();
        sql = sqlExpression.interpret(context);

        // Select SQL
        wheres = new HashMap<>();
        wheres.put("name", "小明");
        context.setParams(null);
        context.setWheres(wheres);
        sqlExpression = new SelectSQLExpression();
        sql = sqlExpression.interpret(context);
    }

}

打印结果:

Insert SQL : insert into user(name,job) values ('小明','Java 工程师')
Delete SQL : delete from user where  1 = 1 and name = '小明'
Update SQL : update user set job = 'Java 高级工程师' where  1 = 1  and name = '小明'
Select SQL : select * from user where  1 = 1  and name = '小明'

上面实现了整个解释器模式的代码,其实咱们在开发中,SQL 解析没有这么去实现,更多是用一个工具类把上面的各个 SQL 解释器的逻辑代码分别实现在不同方法中,如下代码所示。因为咱们可以预见的就这 4 种语法类型,基本上不用什么扩展,用一个工具类就足够了。

class SQLUtil {

    public static String insert(String tableName, Map<String, Object> params) {
        StringBuilder insert = new StringBuilder();
        insert.append("insert into ")
                .append(tableName);

        // 解析 key value
        StringBuilder keys = new StringBuilder();
        StringBuilder values = new StringBuilder();
        keys.append("(");
        values.append("(");
        for (String key : params.keySet()) {
            keys.append(key).append(",");
            values.append("'").append(params.get(key)).append("',");
        }
        keys = keys.replace(keys.length() - 1, keys.length(), ")");
        values = values.replace(values.length() - 1, values.length(), ")");

        // 拼接 keys values
        insert.append(keys)
                .append(" values ")
                .append(values);

        System.out.println("Insert SQL : " + insert.toString());
        return insert.toString();
    }

    public static String update(String tableName, Map<String, Object> params, Map<String, Object> wheres) {
        StringBuilder update = new StringBuilder();
        update.append("update ")
                .append(tableName)
                .append(" set ");

        StringBuilder values = new StringBuilder();
        for (String key : params.keySet()) {
            values.append(key)
                    .append(" = '")
                    .append(params.get(key))
                    .append("',");
        }

        StringBuilder wheresStr = new StringBuilder();
        wheresStr.append(" 1 = 1 ");
        for (String key : wheres.keySet()) {
            wheresStr.append(" and ")
                    .append(key)
                    .append(" = '")
                    .append(wheres.get(key))
                    .append("'");
        }

        update.append(values.substring(0, values.length() - 1))
                .append(" where ")
                .append(wheresStr);

        System.out.println("Update SQL : " + update.toString());
        return update.toString();
    }

    public static String select(String tableName, Map<String, Object> wheres) {
        StringBuilder select = new StringBuilder();
        select.append("select * from ")
                .append(tableName)
                .append(" where ")
                .append(" 1 = 1 ");
        for (String key : wheres.keySet()) {
            select.append(" and ")
                    .append(key)
                    .append(" = '")
                    .append(wheres.get(key))
                    .append("'");
        }
        System.out.println("Select SQL : " + select.toString());
        return select.toString();
    }

    public static String delete(String tableName, Map<String, Object> wheres) {
        StringBuilder delete = new StringBuilder();
        delete.append("delete from ")
                .append(tableName)
                .append(" where ")
                .append(" 1 = 1");
        for (String key : wheres.keySet()) {
            delete.append(" and ")
                    .append(key)
                    .append(" = '")
                    .append(wheres.get(key))
                    .append("'");
        }
        System.out.println("Delete SQL : " + delete.toString());

        return delete.toString();
    }
}

总结

上面用解释器模式实现了 SQL 解释器,然后又指明了实际上咱们开发中大多数是直接一个 SQLUtil 工具类就搞定,并不是说解释器模式没用,想表达的观点是:解释器在工作中很少使用,工作中我们一般遵循的是能用就好策略,满足当前需求,加上一些易扩展性就足够了。解释器模式有比较大的扩展性,就如上面,再加上个建表语句 create table 只需要加一个 CreateTableSQLExpression 就可以轻松实现,不用去改动其他解释器代码。今天的解释器就到讲到这。觉得不错点个赞鼓励鼓励一下。

推荐阅读

行为型模式:备忘录模式

行为型模式:观察者模式

行为型模式:迭代器模式

设计模式系列文章持续更新中,欢迎关注公众号 LieBrother,一起交流学习。

LieBrother

© 著作权归作者所有

LieBrother
粉丝 42
博文 34
码字总数 52621
作品 0
深圳
程序员
私信 提问
加载中

评论(2)

LieBrother
LieBrother
嗯嗯,对这个 SQL 转换理解成翻译也合适
莫在全
莫在全
对SQL而言,感觉叫翻译器更合适吧。
小菜学设计模式——设计模式总结之行为型(2)

1、设计模式总结 设计模式总共23个,但是常用的不到10个,下面就把这23个设计模式进行整理归类,具体如下: 1)创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型...

learn_more
2015/07/10
137
0
一句话总结23种设计模式则

1. 前言 断断续续写了一段时间的设计模式,终于把经典的23种设计模式全写完了。下面对这些设计模式总结一下。 2. 设计原则 设计原则的介绍 : 设计模式的六大原则 即使我们之前没有专门看过这...

四月葡萄
2018/01/05
0
0
PHP之设计模型分类(一)

经典的《设计模式》一书归纳出23种设计模式,本文按《易学设计模式》一书归纳分类如下: 1.创建型模式 前面讲过,社会化的分工越来越细,自然在软件设计方面也是如此,因此对象的创建和对象...

peasant
2016/04/29
81
0
设计模式-创建-singleton(单例)模式

单例模式是一种创建型的模式,适用于 全局只有一个对象的类, 结构图 只有一个静态 实例变量 和一个静态 函数 外部通过Instance() 静态函数来获取 唯一实例 存在的问题: 线程安全性 可用dou...

梦想游戏人
2016/04/28
45
0
设计模式之解释器模式(行为型)

[TOC] 一、模式定义 解释器模式(Interpreter Pattern):定义语言的文法,并且建立一个解释器来解释改语言中的句子,这里的“语言”意思是规定格式和语法的代码,所以解释器模式是一种类行为型...

smileNicky
04/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周二乱弹 —— 吾不好梦中插人

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @鱼豆腐233 :#今日歌曲分享# 分享My Chemical Romance的单曲《I Don't Love You》: 《I Don't Love You》- My Chemical Romance 手机党少年们...

小小编辑
今天
523
12
ss5 vpn 安装(linux版本)

1. 创建一个文件夹 /ss5 你也可以自定义,不过后续的地方需要注意自己的地址 2. 下载ss5文件(如果你的服务器没有安装wget请使用 yum -y install wget 命令安装 如果连yum都没安装自己查去)(下...

太黑_thj
今天
2
0
八、RabbitMQ的集群原理

集群架构 写在前面 RabbitMQ集群是按照低延迟环境设计的,千万不要跨越WAN或者互联网来搭建RabbitMQ集群。如果一定要在高延迟环境下使用RabbitMQ集群,可以参考使用Shovel和Federation工具。...

XuePeng77
今天
9
0
mac系统下,brew 安装mysql,用终端可以连接,navicat却连接不上?

问题: 1.报错? 2059 - Authentication plugin 'caching_sha2_password' cannot be loaded: dlopen(../Frameworks/caching_sha2_password.so, 2): image not found 2.自己通过设置,已经把密......

写bug的攻城狮
昨天
3
0
老生常谈,HashMap的死循环

问题 最近的几次面试中,我都问了是否了解HashMap在并发使用时可能发生死循环,导致cpu100%,结果让我很意外,都表示不知道有这样的问题,让我意外的是面试者的工作年限都不短。 由于HashMap...

群星纪元
昨天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部