行為型模式:解釋器模式

来源:https://www.cnblogs.com/liebrother/archive/2019/04/15/10708603.html
-Advertisement-
Play Games

原文首發: "行為型模式:解釋器模式" 十一大行為型模式之十:解釋器模式。 簡介 姓名 :解釋器模式 英文名 :Interpreter Pattern 價值觀 :不懂解釋到你懂​ 個人介紹 : Given a language, define a representation for its gra ...


原文首發:
行為型模式:解釋器模式

鯊魚

十一大行為型模式之十:解釋器模式。

簡介

姓名 :解釋器模式
英文名 :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 就可以輕鬆實現,不用去改動其他解釋器代碼。今天的解釋器就到講到這。覺得不錯點個贊鼓勵鼓勵一下。

推薦閱讀

行為型模式:備忘錄模式

行為型模式:觀察者模式

行為型模式:迭代器模式


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 項目中經常用到的日期格式化以及金額千分位方法,封裝一下直接拿去用 // 時間格式化export function formatDate(date, fmt) { if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYea ...
  • javascript中toFixed使用的是銀行家舍入規則。 銀行家舍入:所謂銀行家舍入法,其實質是一種四舍六入五取偶(又稱四舍六入五留雙)法。 簡單來說就是:四舍六入五考慮,五後非零就進一,五後為零看奇偶,五前為偶應捨去,五前為奇要進一。但是不論引入toFixed解決浮點數計算精度缺失的問題也好, ...
  • 瞭解JavaScript的同學可能知道,JavaScript語言由於設計原因,導致語言本身存在很多先天性的不足,當然這並非設計者有意的,js語言最初是被設計來作為網頁交互的腳本語言,依照現有的js語法來看,其最初的需求已經完全能夠滿足。互聯網的發展極大的提高了對web端的要求,不僅僅要求簡單的頁面交 ...
  • 1.什麼是XSS http://netsecurity.51cto.com/art/201610/518517.htm https://www.bilibili.com/video/av32599703 ...
  • 谷歌向上滾動滾輪是正值,向下是負值,而火狐正好相反 完整測試代碼,分別用谷歌和火狐瀏覽器測試 ...
  • 閑聊 步入前端切圖仔行列的我曾多次糾結過「到底使用哪種編輯器寫前端好用?」這樣的問題,前前後後嘗試過 Dreamweaver 、HBuilder 、Sublime Text 、Atom 和現在主要使用的 VSCode 。現在回過頭來看,我發現這個問題有了答案,那就是「愛用啥用啥」。(笑…… 今天的主 ...
  • request、out對象使用 一、實驗目的 1、掌握代碼片段中的註釋的應用; 2、掌握JSP腳本標示 Java代碼片段的應用。 二、實驗內容 1、設計教師與學生不同登陸界面,如下圖; 2、驗證碼隨機生成; 3、提交後分別轉向教師頁面和學生頁面進行判斷用戶名和密碼正確性; 4、如果正確,3秒後,轉向 ...
  • 渲染機制 渲染機制主要分為兩部分: 首次渲染和更新渲染。 首次渲染 首先通過一個小例子,來講解首次渲染過程。 程式運行到 時,其中的 babel React.createElement(ClickCounter, null) element`如下: 接下來執行 函數,生成 節點。首先瞭解下 的部分數 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...