解釋器模式

1 四則運算問題

現(xiàn)在有一個四則運算問題需要用代碼實現(xiàn):計算形如a+b-c的值,其中表達式的字母不能為空,并且每個字母都有數(shù)字與它對應,最后求出結果。
傳統(tǒng)的方法是寫一個方法,接收表達式的形式,然后根據(jù)用戶的輸入數(shù)值進行解析,最終計算出結果。但是這種方法不利于擴展,即加入新的運算符需要新增對應方法來解析,容易造成程序的混亂。為了更好地解決這個問題,可以考慮使用解釋器模式。

2 解釋器模式介紹

在編譯原理中,一個算術表達式通過詞法分析器形成詞法單元,然后這些詞法單元再通過語法分析器構建語法分析樹,最終形成一顆抽象的語法分析樹。這里的詞法分析器和語法分析器都可以稱之為解釋器。
解釋器模式(Interpreter Pattern):給定一個語言(表達式),定義它的文法的一種表示,并形成一個解釋器,使用該解釋器來解釋語言中的句子。
角色分析:
1)Context:環(huán)境上下文角色,包含解釋器之外額全局信息
2)AbstractExpression:抽象表達式,聲明一個抽象的解釋操作,這個方法為抽象語法樹中所有的節(jié)點共享
3)TerminalExpression:終結符表達式,實現(xiàn)與文法中的終結符相關的解釋操作
4)NonTerminalExpression:非終結符表達式,為文法中的非終結符實現(xiàn)解釋操作
5)Client:調用方,Context和TerminalExpression可以在這里輸入


解釋器模式類圖

3 代碼實現(xiàn)

首先有一個抽象表達式Expression:

// 表達式抽象類,通過Map獲取到變量的值
public abstract class Expression {
    // 解釋公式和數(shù)值
    public abstract int interpreter(Map<String, Integer> var);
}

終結符表達式繼承自Expression,對應到需求中的字母:

public class VarExpression extends Expression {
    private String key; // key=a,b,c...

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

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

非終結符表達式基類,對應運算符(+、-):

// 抽象的運算符號解析器,每個運算符號都只和自己左右兩個數(shù)字或表達式有關系
public abstract class SymbolExpression extends Expression{
    protected Expression left;
    protected Expression right;

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

加法運算符表達式類:

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

    @Override
    public int interpreter(Map<String, Integer> var) {
        return left.interpreter(var) + right.interpreter(var);
    }
}

減法運算符表達式類:

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

    @Override
    public int interpreter(Map<String, Integer> var) {
        return left.interpreter(var) - right.interpreter(var);
    }
}

環(huán)境上下文類:

public class Calculator {
    private Expression expression;

    public Calculator(String expStr) {
        Stack<Expression> stack = new Stack<>();
        char[] chars = expStr.toCharArray();

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

    public int run(Map<String, Integer> var) {
        return expression.interpreter(var);
    }
}

Client調用:

public class Client {
    public static void main(String[] args) {
        String expStr = "a+b-c";
        Map<String, Integer> var = new HashMap<>();
        var.put("a", 2);
        var.put("b", 3);
        var.put("c", 4);
        Calculator calculator = new Calculator(expStr);
        System.out.println("運算結果:" + expStr + " = " + calculator.run(var));
    }
}

輸出結果:

運算結果:a+b-c = 1
四則運算實現(xiàn)類圖

4 解釋器模式的特點

1)當有一個語言需要解釋執(zhí)行,可將該語言中的句子表示為一個抽象的語法樹,此時可以考慮使用解釋器模式,讓程序具有良好的擴展性。
2)解釋器模式的使用場景:編譯器、運算表達式、正則表達式等。
3)使用解釋器可能會帶來類膨脹,即Context類可能會非常復雜,另外解釋器模式采用遞歸調用的方式,將會導致調試困難、執(zhí)行效率低下等問題。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容