行為型模式:解釋器模式


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

鯊魚

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

簡介

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

推薦閱讀

行為型模式:備忘錄模式

行為型模式:觀察者模式

行為型模式:迭代器模式


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM