設計模式實戰 - 解釋器模式(Interpreter Pattern)

0 講講運算的核心——模型公式及其如何實現

0.1 業務需求:輸入一個模型公式(加、減運算),然後輸入模型中的參數,運算出結果

設計要求

● 公式可以運行時編輯,並且符合正常算術書寫方式,例如a+b-c ● 高擴展性,未來增加指數、開方、極限、求導等運算符號時較少改動 ● 效率可以不用考慮,晚間批量運算

需求不復雜,若僅僅對數字採用四則運算,每個程序員都可以寫出來 但是增加了增加模型公式就複雜了

先解釋一下爲什麼需要公式,而不採用直接計算的方法,例如有如下3個公式 ● 業務種類1的公式:a+b+c-d ● 業務種類2的公式:a+b+e-d ● 業務種類3的公式:a-f 其中,a、b、c、d、e、f參數的值都可以取得,如果使用直接計算數值的方法需要爲每個品種寫一個算法,目前僅僅是3個業務種類,那上百個品種呢?涼透了吧!建立公式,然後通過公式運算纔是王道

以實現加減法公式爲例,說明如何解析一個固定語法邏輯 採用逐步分析方法,帶領大家瞭解實現過程

想想公式中有什麼?運算元素和運算符號

  • 運算元素 指a、b、c等符號,需要具體賦值的對象,也叫做終結符號,爲什麼叫終結符號呢? 因爲這些元素除了需要賦值外,不需要做任何處理,所有運算元素都對應一個具體的業務參數,這是語法中最小的單元邏輯,不可再分
  • 運算符號 就是加減符號,需要我們編寫算法進行處理,每個運算符號都要對應處理單元,否則公式無法運行,運算符號也叫做非終結符號

共同點是都要被解析,不同點是所有運算元素具有相同的功能,可以用一個類表示 而運算符號則是需要分別進行解釋,加法需要加法解析器,減法需要減法解析器

初步分析加減法類圖

這是一個很簡單的類圖 VarExpression用來解析運算元素,各個公式能運算元素的數量是不同的,但每個運算元素都對應一個VarExpression對象 SybmolExpression負責解析符號,由兩個子類

  • AddExpression(負責加法運算)
  • SubExpression(負責減法運算) 解析器的開發工作已經完成了,但是需求還沒有完全實現。我們還需要對解析器進行封裝, 來實現

解析的工作完成了,我們還需要把安排運行的先後順序(加減法不用考慮,但是乘除法呢?注意擴展性),並且還要返回結果,因此我們需要增加一個封裝類來進行封裝處理,由於我們只做運算,暫時還不與業務有關聯,定義爲Calculator

優化後加減法類圖

Calculator的作用是封裝,根據迪米特法則,Client只與直接的朋友Calculator交流,與其他類沒關係

完整加減法類圖

代碼實現

  • Expression抽象類
  • 變量解析器
  • 抽象運算符號解析器

每個運算符號都只和自己左右兩個數字有關係,但左右兩個數字有可能也是一個解析的結果,無論何種類型,都是Expression的實現類,於是在對運算符解析的子類中增加了一個構造函數,傳遞左右兩個表達式。

  • 加法解析器
  • 減法解析器
  • Calculator 我們還需要對解析器進行封裝

Calculator構造函數接收一個表達式,然後把表達式轉化爲char數組,並判斷運算符號,如果是“+”則進行加法運算,把左邊的數(left變量)和右邊的數(right變量)加起來就可以了 那左邊的數爲什麼是在棧中呢?例如這個公式:a+b-c,根據for循環,首先被壓入棧中的應該是有a元素生成的VarExpression對象,然後判斷到加號時,把a元素的對象VarExpression從棧中彈出,與右邊的數組b進行相加,b又是怎麼得來的呢?當前的數組遊標下移一個單元格即可,同時爲了防止該元素再次被遍歷,則通過++i的方式跳過下一個遍歷——於是一個加法的運行結束。減法也採用相同的運行原理。

  • 客戶模擬類 爲了滿足業務要求,我們設置了一個Client類來模擬用戶情況,用戶要求可以擴展,可以修改公式,那就通過接收鍵盤事件來處理

其中,getExpStr是從鍵盤事件中獲得的表達式,getValue方法是從鍵盤事件中獲得表達式中的元素映射值 ● 首先,要求輸入公式。 ● 其次,要求輸入公式中的參數。 ● 最後,運行出結果

我們是不是可以修改公式?當然可以,我們只要輸入公式,然後輸入相應的值就可以了,公式是在運行時定義的,而不是在運行前就制定好的 先公式,然後賦值,運算出結果

需求已經開發完畢,公式可以自由定義,只要符合規則(有變量有運算符合)就可以運算出結果;若需要擴展也非常容易,只要增加BaseSymbolExpression的子類就可以了,這就是解釋器模式。

1 定義與類型

  • 解釋器模式 Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.(給定一門語言,定義它的文法的一種表示,並定義一個解釋器,該解釋器使用該表示來解釋語言中的句子。) 一種按照規定語法進行解析的方案,在現在項目中使用較少

解釋器模式通用類圖

● AbstractExpression——抽象解釋器 具體的解釋任務由各個實現類完成 具體的解釋器分別由TerminalExpressionNon-terminalExpression完成

● TerminalExpression——終結符表達式 實現與文法中的元素相關聯的解釋操作,通常一個解釋器模式中只有一個終結符表達式,但有多個實例,對應不同的終結符 具體到我們例子就是VarExpression類,表達式中的每個終結符都在棧中產生了一個VarExpression對象

● NonterminalExpression——非終結符表達式

文法中的每條規則對應於一個非終結表達式,具體到我們的例子就是加減法規則分別對應到AddExpression和SubExpression兩個類。非終結符表達式根據邏輯的複雜程度而增加,原則上每個文法規則都對應一個非終結符表達式。

● Context——環境角色

具體到我們的例子中是採用HashMap代替。

解釋器是一個比較少用的模式,以下爲其通用源碼,可以作爲參考。抽象表達式通常只有一個方法,如代碼清單27-8所示。

適用場景

優點

缺點

相關設計模式

適配器模式不需要預先知道要適配的規則 而解釋器模式則需要將規則寫好,並根據規則進行解釋

實際應用

Java 正則對象

Spring解析器

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章