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——抽象解釋器
具體的解釋任務由各個實現類完成
具體的解釋器分別由TerminalExpression
和Non-terminalExpression
完成
● TerminalExpression——終結符表達式
實現與文法中的元素相關聯的解釋操作,通常一個解釋器模式中只有一個終結符表達式,但有多個實例,對應不同的終結符
具體到我們例子就是VarExpression
類,表達式中的每個終結符都在棧中產生了一個VarExpression對象
● NonterminalExpression——非終結符表達式
文法中的每條規則對應於一個非終結表達式,具體到我們的例子就是加減法規則分別對應到AddExpression和SubExpression兩個類。非終結符表達式根據邏輯的複雜程度而增加,原則上每個文法規則都對應一個非終結符表達式。
● Context——環境角色
具體到我們的例子中是採用HashMap代替。
解釋器是一個比較少用的模式,以下爲其通用源碼,可以作爲參考。抽象表達式通常只有一個方法,如代碼清單27-8所示。
適用場景
優點
缺點
相關設計模式
適配器模式不需要預先知道要適配的規則 而解釋器模式則需要將規則寫好,並根據規則進行解釋