编程知识 cdmana.com

Design mode (20) -- interpreter mode

1. The above summary

「 Make up a missed lesson 」 In progress : Design pattern series

2. Interpreter mode

The Interpreter pattern is similar to the previous visitor pattern , Of course , I said more like difficult, more like , And the utilization rate is really low , Basically no scenes used , Visitor mode also has some usage scenarios , Interpreter mode , We don't write interpreters , This thing JVM They've helped us realize , We can't do it ourselves .

Common interpreters are JVM For us Java The interpreter of language , And we often use MySQL , There are also built-in SQL Interpreter .

But it's useless. It's useless , We can still learn the corresponding pattern .

2.1 Definition

Interpreter mode (Interpreter Pattern) It's a program that parses according to the prescribed grammar , Its definition is as follows :

Given a language, define a representation for its grammar along with an
interpreter that uses the representation to interpret sentences in the language.( Give a language , A representation of the grammar that defines it , And define an interpreter , The interpreter uses the representation to interpret sentences in the language . )

2.2 Generic class diagram

  • AbstractExpression abstract interpreter : The specific interpretation task is completed by each implementation class , The specific interpreter is composed of TerminalExpression and NonterminalExpression complete .
  • TerminalExpression Terminator expression : Implement the interpretation operations associated with elements in grammar , Usually there is only one terminator expression in an Interpreter pattern , But there are multiple instances , Corresponding to different terminators .
  • NonterminalExpression Nonterminal expression : Each rule in grammar corresponds to a non terminal expression , Specifically, our example is that the addition and subtraction rules correspond to AddExpression and SubExpression Two classes . Nonterminal expressions increase based on the complexity of the logic , In principle, each grammar rule corresponds to a nonterminal expression .
  • Context Environmental roles

2.3 General code

Abstract expression

public abstract class Expression {
    
    abstract Object interpreter(Context ctx);
}

Abstract expressions are relatively simple , There is usually only one way , But it's a generative grammar set ( It's also called a grammar tree ) The key to , Each grammar set completes the specified parsing task , It's called recursively , Finally, it is resolved by the smallest syntax unit .

Terminator expression

public class TerminalExpression extends Expression {
    
    //  Usually there is only one terminator expression ,  But there are multiple objects 
    public Object interpreter(Context context) {
    
        return null;
    }
}

Terminator expressions are simpler , It mainly deals with the transformation of scene elements and data .

Nonterminal expression

public class NonterminalExpression extends Expression {
    
    //  Every nonterminal expression has dependencies on other expressions 
    public NonterminalExpression(Expression ...expressions) {
    
    }
    @Override
    Object interpreter(Context ctx) {
    
        //  Do grammar processing 
        return null;
    }
}

Each nonterminal expression represents a grammar rule , And every grammar rule only cares about the result of grammar rules around itself ( Attention is the result ) , Therefore, each nonterminal expression calls its own peripheral nonterminal expression , And then finally 、 The smallest grammar rule is the terminator expression , This is the concept of the terminator expression , I can't participate in grammar operations smaller than myself .

client

public class Client {
    
    public static void main(String[] args) {
    
        Context ctx = new Context();
        Stack<Expression> stack = null;
        for(int i = 0; i < args.length; i++){
    
            //  Make grammatical judgment ,  And generate recursive calls 
        }
        //  Generate a complete grammar tree ,  By each concrete grammar analysis carries on the analysis 
        Expression exp = stack.pop();
        // Specific elements enter the scene 
        exp.interpreter(ctx);
    }
}

2.4 advantage

Interpreter is a simple parsing tool , Its most significant advantage is scalability , To modify the syntax rules, just modify the corresponding non terminal expression , If extended grammar , You just need to add a non terminator class .

2.5 shortcoming

  • Interpreter mode causes class inflation .
  • The interpreter mode uses recursive call methods , It will lead to very complex debugging .
  • A lot of loops and recursion are used , Efficiency is a problem that cannot be ignored .

3. arithmetic

Simply use the interpreter mode to implement the addition and subtraction operation .

First define a computing class , As a parser wrapper :

public class Calculator {
    
    private Expression expression;

    //  Constructors , Pass on the reference and analyze 
    public Calculator(String expStr) {
    
        //  Arrange the order of operations 
        Stack<Expression> stack = new Stack<>();
        //  The expression is split into character arrays 
        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);
    }
}

Next is an abstract expression :

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

Here's a variable parser :

public class VarExpression extends Expression {
    
    private String key;
    public VarExpression(String key) {
    
        this.key = key;
    }
    @Override
    int interpreter(HashMap<String, Integer> var) {
    
        return var.get(this.key);
    }
}

The main function of the variable parser is from map Take the data one by one .

And then there's a symbolic parser :

public class SymbolExpression extends Expression {
    
    protected Expression left;
    protected Expression right;

    public SymbolExpression(Expression left, Expression right) {
    
        this.left = left;
        this.right = right;
    }
    @Override
    int interpreter(HashMap<String, Integer> var) {
    
        return 0;
    }
}

Because it's an arithmetic symbol , And each operation symbol is only related to its left and right numbers , The left and right numbers may also be the result of an analysis , Whatever type , All are Expression Class .

Next are two concrete arithmetic symbol parsers , A plus parser and a minus parser :

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

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

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

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

Finally, our client class :

public class Client {
    
    public static void main(String[] args) throws IOException {
    
        String expStr = getExpStr();
        HashMap<String, Integer> var = getValue(expStr);
        Calculator calculator = new Calculator(expStr);
        System.out.println(" Calculation results :" + expStr + "=" + calculator.run(var));
    }

    public static String getExpStr() throws IOException {
    
        System.out.print(" Please enter the expression :");
        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(" Please enter " + String.valueOf(ch) + " Value :");
                    String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
                    map.put(String.valueOf(ch), Integer.valueOf(in));
                }
            }
        }
        return map;
    }
}

The results are as follows :

 Please enter the expression :a+b-c
 Please enter a Value :10
 Please enter b Value :20
 Please enter c Value :13
 Calculation results :a+b-c=17

Interpreter pattern is rarely used in actual system development , Because it leads to efficiency 、 Performance and maintenance issues , Generally, it can be found in large and medium-sized framework projects , For example, some data analysis tools 、 Report design tool 、 Scientific computing tools, etc .

If you encounter a scenario where you are sure you want to use a parser , Think about Expression4J 、 MESP(Math Expression String Parser) 、 Jep Open source parsing toolkit , It's incredibly powerful , And it's very easy to use , The efficiency is good too , There's no problem with most mathematical operations , There's no need to write an interpreter from scratch .

Your scanning attention , It's the biggest encouragement for Xiaobian to insist on originality :)

版权声明
本文为[Geek excavator]所创,转载请带上原文链接,感谢
https://cdmana.com/2020/12/20201224215727708T.html

Scroll to Top