【解釋器設計模式詳解】C/Java/Go/JS/TS/Python不同語言實現

来源:https://www.cnblogs.com/letjs/archive/2023/04/12/17310678.html
-Advertisement-
Play Games

簡介 解釋器模式(Interpreter Pattern)是一種行為型設計模式。這種模式實現了一個表達式介面,該介面解釋一個特定的上下文。這種模式常被用在 SQL 解析、符號處理引擎等。 解釋器模式常用於對簡單語言的編譯或分析實例中,為了掌握好它的結構與實現,必須先瞭解編譯原理中的“文法、句子、語法 ...


簡介

解釋器模式(Interpreter Pattern)是一種行為型設計模式。這種模式實現了一個表達式介面,該介面解釋一個特定的上下文。這種模式常被用在 SQL 解析、符號處理引擎等。

解釋器模式常用於對簡單語言的編譯或分析實例中,為了掌握好它的結構與實現,必須先瞭解編譯原理中的“文法、句子、語法樹”等相關概念。

 

作用

  1. 可擴展性比較好,靈活,增加了新的解釋表達式的方式,易於實現簡單文法。
  2. 在語法樹中的每個表達式節點類都是相似的,所以實現其文法較為容易。

 

實現步驟

  1. 創建抽象表達式介面(Expression),各種表達式都要實現該介面。
  2. 分別創建最終表達式和非最終表達式。最終表達式(這裡是VarExpression)沒有子級,直接解釋表達式。非最終表達式(這裡是AddExpression和SubtractExpression)是維護子表達式的容器,並將解釋請求轉發給這些表達式。
  3. 創建上下文環境類(這裡是Context),用來表達式求值時構建執行環境。
  4. 客戶端調用時先建立執行上下文環境,然後聲明變數,再進行計算。

 

UML

 

 

Java代碼

 

抽象表達式介面

 

// Expression.java 抽象表達式介面,根據業務場景規範表達式
public interface Expression {
  public int interpret(Context context);
}

 

具體表達式實現

 

// AddExpression.java 具體表達式,實現了抽象表達式介面
public class AddExpression implements Expression {

    private Expression exprOne = null;
    private Expression exprTwo = null;

    public AddExpression(Expression exprOne, Expression exprTwo) {
        this.exprOne = exprOne;
        this.exprTwo = exprTwo;
    }

    // 覆蓋表達式,執行context對象
    @Override
    public int interpret(Context context) {
        System.out.println(this.getClass().getName() + "::interpret() [context = " + context.getClass().getName() + "]");
        return exprOne.interpret(context) + exprTwo.interpret(context);
    }
}

 

// SubtractExpression.java 具體表達式,實現了抽象表達式介面
public class SubtractExpression implements Expression {

  private Expression exprOne = null;
  private Expression exprTwo = null;

  public SubtractExpression(Expression exprOne, Expression exprTwo) {
    this.exprOne = exprOne;
    this.exprTwo = exprTwo;
  }

  // 覆蓋表達式,執行context對象
  @Override
  public int interpret(Context context) {
    System.out.println(this.getClass().getName() + "::interpret() [context = " + context.getClass().getName() + "]");
    return exprOne.interpret(context) - exprTwo.interpret(context);
  }
}

 

// VarExpression.java 變數表達式,或者叫終端表達式,其他表達式求值時通過層層追溯最後指向這裡
// 變數與執行環境的Key對應,最終會通過key獲取傳入的值
public class VarExpression implements Expression {
  private String key;

  public VarExpression(String key) {
    this.key = key;
  }

  @Override
  // 覆蓋表達式,根據key獲取變數
  public int interpret(Context context) {
    return context.get(key);
  }
}

 

執行環境類

 

// Context.java 構建可執行環境上下文
public class Context {

   private Map<String, Integer> map = new HashMap<>();

   public Context(String key, int value) {
      this.add(key, value);
   }

   public Context() {
   }

   public void add(String key, int value) {
      map.put(key, value);
   }

   public int get(String key) {
      return map.get(key);
   }
}

 

// Application.java 調用程式,組織各種解釋器
    /*
     * 解釋器模式先構建執行上下文Context,然後構建一個最終的獲取值的表達式VarExpression,這就構成了含上下文和變數-值的基本環境。
     * 再將基本環境放到工具表達式里AddExpression或SubtractExpreesion進行計算,最終得到結果。
     */

   // 構建兩個數相加的例子
   public static int addTwo(int one, int two) {
      // 構建執行上下文環境
      Context context = new Context();
      context.add("one", one);
      context.add("two", two);

      // 構建表達式
      VarExpression varOne = new VarExpression("one");
      VarExpression varTwo = new VarExpression("two");

      // 再構建變數來進行計算,看起來啰嗦,但這樣構建多種不同表達式計算就變得簡單
      Expression result = new AddExpression(varOne, varTwo);
      return result.interpret(context);
   }

   // 構建連加計算的例子
   public static int addMore(int... numbers) {
      if (numbers.length <= 1) {
         return numbers[0];
      }

      Context context = new Context();
      // 構建執行環境
      for (int num : numbers) {
         context.add("num" + num, num);
      }

      // 先取出前兩個作為計算基礎
      VarExpression varOne = new VarExpression("num" + numbers[0]);
      VarExpression varTwo = new VarExpression("num" + numbers[1]);
      // 再構建表達式,先賦值前兩個
      Expression expression = new AddExpression(varOne, varTwo);

      // 如果只有兩個數則直接返回計算結果
      if (numbers.length == 2) {
         return expression.interpret(context);
      }

      // 如果數量超過兩個則累加表達式再求值
      for (int i = 2; i < numbers.length; i++) {
         Expression nextExpression = new VarExpression("num" + numbers[i]);
         // 表達式不斷累加
         expression = new AddExpression(expression, nextExpression);
      }

      return expression.interpret(context);
   }

   // 計算前兩個數相加,再減去後一個數的計算例子
   public static int addAndSubtract(int one, int two, int three) {
      // 構建執行上下文環境,有3個可操作的域
      Context context = new Context();
      context.add("one", one);
      context.add("two", two);
      context.add("three", three);

      // 構建表達式,有3個變數
      VarExpression varOne = new VarExpression("one");
      VarExpression varTwo = new VarExpression("two");
      VarExpression varThree = new VarExpression("three");


      // 再構建計算步驟,前兩個用加法
      Expression result = new AddExpression(varOne, varTwo);
      result = new SubtractExpression(result, varThree);
      // 第3個用減法
      return result.interpret(context);
   }

 

測試調用

 

    /**
     * 解釋器模式實現了一個表達式介面,該介面可以解釋一個特定的上下文的變數和語句。
     * 也就是先定義上下文,然後定義變數,再使用表達式進行求值。相當可以構造一個簡單的語法解析器。
     */

    int result1 = Application.addTwo(1, 2);
    System.out.println("result1: " + result1);

    int result2 = Application.addMore(1, 2, 3, 4, 5);
    System.out.println("result2: " + result2);

    int result3 = Application.addAndSubtract(3, 4, 5);
    System.out.println("result3: " + result3);

 

Go代碼

 

抽象表達式介面

 

// Expression.go 抽象表達式介面,根據業務場景規範表達式
type Expression interface {
  Interpret(context Context) int
}

 

具體表達式實現

 

// AddExpression.go 具體表達式,實現了抽象表達式介面
type AddExpression struct {
  exprOne Expression
  exprTwo Expression
}

func (a *AddExpression) Init(exprOne Expression, exprTwo Expression) {
  a.exprOne = exprOne
  a.exprTwo = exprTwo
}

// 覆蓋表達式,執行context對象
func (a *AddExpression) Interpret(context Context) int {
  fmt.Println("AddExpression::interpret() [context = Context]")
  return a.exprOne.Interpret(context) + a.exprTwo.Interpret(context)
}

 

// SubtractExpression.go 具體表達式,實現了抽象表達式介面
type SubtractExpression struct {
  exprOne Expression
  exprTwo Expression
}

func (s *SubtractExpression) Init(exprOne Expression, exprTwo Expression) {
  s.exprOne = exprOne
  s.exprTwo = exprTwo
}

// 覆蓋表達式,執行context對象
func (s *SubtractExpression) Interpret(context Context) int {
  fmt.Println("SubtractExpression::Interpret() [context = Context]")
  return s.exprOne.Interpret(context) - s.exprTwo.Interpret(context)
}

 

// VarExpression.go 變數表達式,或者叫終端表達式,其他表達式求值時通過層層追溯最後指向這裡
// 變數與執行環境的Key對應,最終會通過key獲取傳入的值
type VarExpression struct {
  key string
}

// 覆蓋表達式,根據key獲取變數
func (v VarExpression) Interpret(context Context) int {
  return context.Get(v.key)
}

 

執行環境類

 

// Context.go 構建可執行環境上下文
type Context struct {
  Map map[string]int
}

func (c *Context) Init(key string, value int) {
  c.Map = make(map[string]int)
  c.Add(key, value)
}

func (c *Context) Add(key string, value int) {
  if c.Map == nil {
    c.Map = make(map[string]int)
  }
  c.Map[key] = value
}

func (c *Context) Get(key string) int {
  return c.Map[key]
}

 

// Application.go 調用程式,組織各種解釋器
    /*
     * 解釋器模式先構建執行上下文Context,然後構建一個最終的獲取值的表達式VarExpression,這就構成了含上下文和變數-值的基本環境。
     * 再將基本環境放到工具表達式里AddExpression或SubtractExpreesion進行計算,最終得到結果。
     */

type Application struct {
}

// 構建兩個數相加的例子
func (a *Application) AddTwo(one int, two int) int {
  // 構建執行上下文環境
  var context = new(Context)
  context.Add("one", one)
  context.Add("two", two)

  // 構建表達式
  var varOne = &VarExpression{key: "one"}
  var varTwo = &VarExpression{key: "two"}

  // 再構建變數來進行計算,看起來啰嗦,但這樣構建多種不同表達式計算就變得簡單
  var result = &AddExpression{
    exprOne: varOne,
    exprTwo: varTwo,
  }
  return result.Interpret(*context)
}

// 構建連加計算的例子
func (a *Application) AddMore(numbers ...int) int {
  if len(numbers) <= 1 {
    return numbers[0]
  }

  var context = new(Context)
  // 構建執行環境
  for _, num := range numbers {
    context.Add(""+strconv.Itoa(num), num)
  }

  // 先取出前兩個作為計算基礎
  var varOne = &VarExpression{key: "" + strconv.Itoa(numbers[0])}
  var varTwo = &VarExpression{key: "" + strconv.Itoa(numbers[1])}
  // 再構建表達式,先賦值前兩個
  var expression = &AddExpression{
    exprOne: varOne,
    exprTwo: varTwo,
  }

  // 如果只有兩個數則直接返回計算結果
  if len(numbers) == 2 {
    return expression.Interpret(*context)
  }

  // 如果數量超過兩個則累加表達式再求值
  for i := 2; i < len(numbers); i++ {
    var nextExpression = &VarExpression{
      key: "" + strconv.Itoa(numbers[i]),
    }
    // 表達式不斷累加
    expression = &AddExpression{
      exprOne: expression,
      exprTwo: nextExpression,
    }
  }

  return expression.Interpret(*context)
}

// 計算前兩個數相加,再減去後一個數的計算例子
func (a *Application) AddAndSubtract(one int, two int, three int) int {
  // 構建執行上下文環境,有3個可操作的域
  var context = &Context{}
  context.Add("one", one)
  context.Add("two", two)
  context.Add("three", three)

  // 構建表達式,有3個變數
  var varOne = &VarExpression{key: "one"}
  var varTwo = &VarExpression{key: "two"}
  var varThree = &VarExpression{key: "three"}

  // 再構建計算步驟,前兩個用加法
  var result Expression

  result = &AddExpression{
    exprOne: varOne,
    exprTwo: varTwo,
  }
  result = &SubtractExpression{
    exprOne: result,
    exprTwo: varThree,
  }
  // 第3個用減法
  return result.Interpret(*context)
}

 

測試調用

 

    /*
     * 解釋器模式先構建執行上下文Context,然後構建一個最終的獲取值的表達式VarExpression,這就構成了含上下文和變數-值的基本環境。
     * 再將基本環境放到工具表達式里AddExpression或SubtractExpreesion進行計算,最終得到結果。
     */
    
  /**
   * 解釋器模式實現了一個表達式介面,該介面可以解釋一個特定的上下文的變數和語句。
   * 也就是先定義上下文,然後定義變數,再使用表達式進行求值。相當可以構造一個簡單的語法解析器。
   */

  var application = &src.Application{}
  var result1 = application.AddTwo(1, 2)
  fmt.Println("result1: ", result1)

  var result2 = application.AddMore(1, 2, 3, 4, 5)
  fmt.Println("result2: ", result2)

  var result3 = application.AddAndSubtract(3, 4, 5)
  fmt.Println("result3: ", result3)

 

C代碼

 

func.h head文件

 

// 構建可執行環境上下文
typedef struct Context
{
    char **keys;
    int 
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 首先說明簡易版是只有一個 倒計時 和一個 進度條,頁面載入後自動開始計時,下次計時需要手動刷新頁面。 後續會更新實現完整的倒計時功能的文章 前期準備 前端框架 你需要準備一些前端框架:Bootstrap4 和 jQuery 安裝方法請自行查閱官方文檔或教程 Bootstrap4:https://v4 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言: 梳理了一下項目中的PWA的相關用法,下麵我會正對vue2和vue3的用法進行一些教程示例,引入離線緩存機制,即使你斷網,也能訪問頁面。一旦用戶訪問了我們的網頁,我們就像牛皮糖一樣粘連著他,他永遠都可以訪問,即使斷網也能訪問。有一天 ...
  • 作者:京東零售 周明亮 寫在前面 這裡我們初步提到了一些基礎概念和應用: 分析器 抽象語法樹 AST AST 在 JS 中的用途 AST 的應用實踐 有了初步的認識,還有常規的代碼改造應用實踐,現在我們來詳細說說使用 AST, 如何進行代碼改造? Babel AST 四件套的使用方法 其實在解析 A ...
  • 當瀏覽器載入網頁時,通常會遵循一個預設的流程,先載入 HTML、CSS 和 JavaScript,然後再載入圖片、音頻、視頻等資源。這個預設的流程可能會導致網頁載入速度變慢,用戶體驗不佳。因此,可以使用一些技術來優化網頁載入的速度,其中之一就是按需載入。 按需載入是指根據用戶實際需要,動態地載入資源 ...
  • 1.準備工作:HbuiderX + 微信開發者工具下載安裝+小程式賬號申請開通(這裡就不例舉了,可以看同賬號uniapp小程式開發準備) 2.創建項目 新版本的HbuilderX點擊新建項目——選擇uni-app——選擇預設模板——輸入項目名稱——選擇Vue版本(此隨筆是前後端分離開發,不使用Uni ...
  • css基礎:塊元素、內聯元素、內聯塊元素 CSS中,html中的標簽元素大體被分為三種不同的類型:塊狀元素、內聯元素(又叫行內元素)和內聯塊狀元素。 1.常用的塊狀元素有: <div>、<p>、<h1>-<h6>、<ol>、<ul>、<dl>、<table>、<address>、<blockquot ...
  • 本文從攻擊者角度和防禦者角度詳細解析前端代碼安全與混淆的相關知識,總結了大部分攻擊者共同點以及如何應對普通開發者外掛程式和Pyhton 爬蟲 ...
  • 讓對象保持消息靈通 #01需求 一個WeatherData對象負責追蹤目前的天氣狀況(溫度,濕度,氣壓)。希望你們能建立一個應用,有三種佈告板,分別顯示目前的狀況、氣象統計及簡單的預報。當WeatherObject對象獲得最新的測量數據時,三種佈告板必須實時更新。而且,這是一個可以擴展的氣象站,We ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...