Java設(shè)計模式之 [19] 行為型模式 - 解釋器模式

簡介

1.在編譯原理中,一個算數(shù)表達(dá)式通常是詞法分析器形成詞法單元,而后這些詞法單元在通過 語法分析器 構(gòu)建語法分析樹,最終形成一個抽象的語法分析樹.這里的詞法分析器和語法分析器都可以看成是解釋器
2.解釋器模式(Interpreter Pattern):是指定一個語言(表達(dá)式),定義它的文法的一種表示,并定義一個解釋器,使用該解釋器來解釋語言中的句子(表達(dá)式)
3.應(yīng)用場景

  • 應(yīng)用可以將一個需要執(zhí)行的語言中的句子表示為一個抽象語法樹.
  • 一些重復(fù)出現(xiàn)的問題可以用一種簡單的語言來表達(dá)
  • 一個簡單的語法需要解釋的場景
    4.這樣的例子還有:比如編譯器.運(yùn)算表達(dá)式,正則表達(dá)式,機(jī)器人等
原理類圖與角色分析

1.類圖


類圖

2.角色分析
Context:是環(huán)境角色,含有解釋器之外的全局信息
AbstractExpression:抽象表達(dá)式,聲明一個抽象的解釋操作,這個方法為抽象語法樹中所有節(jié)點(diǎn)共享
TerminalExpression: 為終結(jié)者表達(dá)式.實現(xiàn)與文法中的終結(jié)符相關(guān)的解釋操作
NoTerminalExpression : 為非終結(jié)者表達(dá)式,為文法中的非終結(jié)符實現(xiàn)的解釋操作
說明:輸入Context 和 TerminalExpression信息通過Cilent 輸入即可

案例分析 四則運(yùn)算問題

通過解釋器模式來實現(xiàn)四則運(yùn)算,如計算 a+b-c 的值 具體要求
1.先輸入表達(dá)式的形式 比如 a+b+c-d-e 要求表達(dá)式的字母不能重復(fù),
2,分別輸入 a,b,c,d,e 的值
3.然后計算出結(jié)果


計算結(jié)果
問題分析

1.編寫一個方法,接收表達(dá)式的形式,然后根據(jù)用戶輸入的數(shù)值進(jìn)行解析計算,得到結(jié)果
2.問題分析:如果輸入新的運(yùn)算符.比如 / * 等等 不利于拓展 另外一個方法來解析會造成程序結(jié)構(gòu)混亂
3.解決方案:可以考慮使用解釋器模式 即 : 表達(dá)式 ==>> 解釋器 (可以有多種) ==>> 結(jié)果

解釋器模式實現(xiàn)四則運(yùn)算

1.思路和圖解分析


類圖

2.代碼實現(xiàn)

public class AddExpression extends SymbolExpression  {

    public AddExpression(Expression left, Expression right) {
        super(left, right);
    }

    public int interpreter(HashMap<String, Integer> var) {
        return super.left.interpreter(var) + super.right.interpreter(var);
    }
}
public class Calculator {

    // 定義表達(dá)式
    private Expression expression;

    // 構(gòu)造函數(shù)傳參脐区,并解析
    public Calculator(String expStr) {
        // 安排運(yùn)算先后順序
        Stack<Expression> stack = new Stack<>();
        char[] charArray = expStr.toCharArray();

        Expression left = null;
        Expression right = null;
        for (int i = 0; i < charArray.length; i++) {
            switch (charArray[i]) {
            case '+': //
                left = stack.pop();//
                right = new VarExpression(String.valueOf(charArray[++i]));//
                stack.push(new AddExpression(left, right));//
                break;
            case '-': // 
                left = stack.pop();
                right = new VarExpression(String.valueOf(charArray[++i]));
                stack.push(new SubExpression(left, right));
                break;
            default: 
                stack.push(new VarExpression(String.valueOf(charArray[i])));
                break;
            }
        }
        this.expression = stack.pop();
    }

    public int run(HashMap<String, Integer> var) {
        return this.expression.interpreter(var);
    }
}
public abstract class Expression {

    public abstract int interpreter(HashMap<String, Integer> var);
}
public class SubExpression extends SymbolExpression {

    public SubExpression(Expression left, Expression right) {
        super(left, right);
    }

    public int interpreter(HashMap<String, Integer> var) {
        return super.left.interpreter(var) - super.right.interpreter(var);
    }
}
/**
 * 抽象運(yùn)算符號解析器 這里阔逼,每個運(yùn)算符合都只和自己左右兩個數(shù)字有關(guān)系,
 * 但左右兩個數(shù)字有可能也是一個解析的結(jié)果渠旁,無論何種類型,都是Expression類的實現(xiàn)類
 * 
 * @author Administrator
 *
 */
public class SymbolExpression extends Expression {

    protected Expression left;
    protected Expression right;

    public SymbolExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpreter(HashMap<String, Integer> var) {
        // TODO Auto-generated method stub
        return 0;
    }
}
public class VarExpression extends Expression {

    private String key; 

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

    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return var.get(this.key);
    }
}

測試

public class ClientTest {

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        String expStr = getExpStr(); 
        HashMap<String, Integer> var = getValue(expStr);
        Calculator calculator = new Calculator(expStr);
        System.out.println("運(yùn)算結(jié)果:" + expStr + "=" + calculator.run(var));
    }

    // 獲得表達(dá)式
    public static String getExpStr() throws IOException {
        System.out.print("請輸入表達(dá)式:");
        return (new BufferedReader(new InputStreamReader(System.in))).readLine();
    }

    // 獲得值映射
    public static HashMap<String, Integer> getValue(String expStr) throws IOException {
        HashMap<String, Integer> map = new HashMap<>();

        for (char ch : expStr.toCharArray()) {
            if (ch != '+' && ch != '-') {
                if (!map.containsKey(String.valueOf(ch))) {
                    System.out.print("請輸入" + String.valueOf(ch) + "的值:");
                    String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
                    map.put(String.valueOf(ch), Integer.valueOf(in));
                }
            }
        }

        return map;
    }
}

測試結(jié)果

請輸入表達(dá)式:a+b+c-d-e
請輸入a的值:10
請輸入b的值:50
請輸入c的值:12
請輸入d的值:78
請輸入e的值:20
運(yùn)算結(jié)果:a+b+c-d-e=-26
解釋器設(shè)計模式在Spring框架應(yīng)用的源碼分析

1.Spring框架中,SpelExpressionParser 就使用到解釋器模式

import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class Interpreter {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //創(chuàng)建一個 Parser 對象
        SpelExpressionParser parser = new SpelExpressionParser();
        //
        //通過 Parser 對象 獲取到一個Expression對象
        //會根據(jù)不同的  Parser 對象 船逮,返回不同的 Expression對象
        Expression expression = parser.parseExpression("10 * (2 + 1) * 1 + 66"); //96
        int result = (Integer) expression.getValue();
        System.out.println(result);

    }

}
解釋器模式的注意事項和細(xì)節(jié)

1.當(dāng)有一個語言需要解釋執(zhí)行.可將該語言中的句子表示為一個抽象語法樹,就可以考慮使用編譯器模式,讓程序具有良好的拓展性
2.應(yīng)用場景:編譯器,運(yùn)算表達(dá)式計算.正則表達(dá)式,機(jī)器人等
3.使用解釋器可能帶來的問題:解釋器模式會引起類膨脹,解釋器模式采用遞歸調(diào)用方法,將會導(dǎo)致調(diào)試程序非常復(fù)雜,效率可能降低

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末顾腊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子挖胃,更是在濱河造成了極大的恐慌杂靶,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冠骄,死亡現(xiàn)場離奇詭異伪煤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)凛辣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門抱既,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扁誓,你說我怎么就攤上這事防泵。” “怎么了蝗敢?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵捷泞,是天一觀的道長。 經(jīng)常有香客問我寿谴,道長锁右,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任讶泰,我火速辦了婚禮咏瑟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘痪署。我一直安慰自己码泞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布狼犯。 她就那樣靜靜地躺著余寥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悯森。 梳的紋絲不亂的頭發(fā)上宋舷,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天,我揣著相機(jī)與錄音瓢姻,去河邊找鬼肥缔。 笑死,一個胖子當(dāng)著我的面吹牛汹来,可吹牛的內(nèi)容都是我干的续膳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼收班,長吁一口氣:“原來是場噩夢啊……” “哼坟岔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起摔桦,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤社付,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后邻耕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸥咖,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年兄世,在試婚紗的時候發(fā)現(xiàn)自己被綠了啼辣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡御滩,死狀恐怖鸥拧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情削解,我是刑警寧澤富弦,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站氛驮,受9級特大地震影響腕柜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜矫废,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一盏缤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧磷脯,春花似錦蛾找、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至俩功,卻和暖如春幻枉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背诡蜓。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工熬甫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蔓罚。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓椿肩,卻偏偏與公主長得像瞻颂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子郑象,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容