Dr. Yan Hong's book "JAVA and Patterns" begins with a description of the Interpreter pattern:
Interpreter pattern is a behavioral pattern for classes. Given a language, the interpreter pattern defines a representation of its grammar and provides an interpreter. Clients can use this interpreter to interpret sentences in this language.
The structure of the interpreter mode
Let's take a schematic system as an example to discuss the structure of the interpreter mode. The system structure diagram is as follows:
The roles involved in the pattern are as follows:
(1) Abstract expression (Expression) role: Declare an abstract interface that all concrete expression roles need to implement. This interface is mainly an interpret() method, called an interpretation operation.
(2) Terminal Expression role: implements the interface required by the abstract expression role, mainly an interpret() method; each terminal symbol in the grammar has a specific terminal expression corresponding to it . For example, there is a simple formula R=R1+R2, in which R1 and R2 are terminal symbols, and the corresponding interpreter that parses R1 and R2 is a terminal expression.
(3) Nonterminal Expression role: Each rule in the grammar requires a specific nonterminal expression. Nonterminal expressions are generally operators or other keywords in the grammar, such as formulas. In R=R1+R2, "+" is a non-terminal symbol, and the interpreter that parses "+" is a non-terminal symbol expression.
(4) Context role: The task of this role is generally to store the specific values corresponding to each terminal symbol in the grammar. For example, R=R1+R2, we assign a value of 100 to R1 and a value of 200 to R2. This information needs to be stored in the environment role. In many cases, it is enough for us to use Map to act as the environment role.
In order to illustrate the implementation of the interpreter mode, here is the simplest grammar and the corresponding implementation of the interpreter mode, which is to simulate the operation and evaluation of Boolean expressions in the Java language.
Terminal symbols in this language are Boolean variables, that is, the constants true and false. Non-terminal expressions include Boolean expressions such as operators and, or and not. This simple grammar is as follows:
Copy the code code as follows:
Expression ::= Constant | Variable | Or | And | Not
And ::= Expression 'AND' Expression
Or ::= Expression 'OR' Expression
Not ::= 'NOT' Expression
Variable ::= any identifier
Constant ::= 'true' | 'false'
The structure diagram of the interpreter mode is as follows:
source code
abstract expression role
Copy the code code as follows:
public abstract class Expression {
/**
* Subject to the environment, this method interprets any given expression
*/
public abstract boolean interpret(Context ctx);
/**
* Check whether two expressions are structurally the same
*/
public abstract boolean equals(Object obj);
/**
* Returns the hash code of the expression
*/
public abstract int hashCode();
/**
* Convert the expression into a string
*/
public abstract String toString();
}
A Constant object represents a Boolean constant
Copy the code code as follows:
public class Constant extends Expression{
private boolean value;
public Constant(boolean value){
this.value = value;
}
@Override
public boolean equals(Object obj) {
if(obj != null && obj instanceof Constant){
return this.value == ((Constant)obj).value;
}
return false;
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
@Override
public boolean interpret(Context ctx) {
return value;
}
@Override
public String toString() {
return new Boolean(value).toString();
}
}
A Variable object represents a named variable. The code is as follows:
public class Variable extends Expression {
private String name;
public Variable(String name){
this.name = name;
}
@Override
public boolean equals(Object obj) {
if(obj != null && obj instanceof Variable)
{
return this.name.equals(
((Variable)obj).name);
}
return false;
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
@Override
public String toString() {
return name;
}
@Override
public boolean interpret(Context ctx) {
return ctx.lookup(this);
}
}
The And class represents the logical "AND" operation, which represents the operation of giving a new Boolean expression from two Boolean expressions through the logical "AND" operation.
Copy the code code as follows:
public class And extends Expression {
private Expression left,right;
public And(Expression left, Expression right){
this.left = left;
this.right = right;
}
@Override
public boolean equals(Object obj) {
if(obj != null && obj instanceof And)
{
return left.equals(((And)obj).left) &&
right.equals(((And)obj).right);
}
return false;
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
@Override
public boolean interpret(Context ctx) {
return left.interpret(ctx) && right.interpret(ctx);
}
@Override
public String toString() {
return "(" + left.toString() + " AND " + right.toString() + ")";
}
}
The Or class represents the logical "OR" operation, which represents the operation of giving a new Boolean expression from two Boolean expressions through the logical "OR" operation.
Copy the code code as follows:
public class Or extends Expression {
private Expression left,right;
public Or(Expression left, Expression right){
this.left = left;
this.right = right;
}
@Override
public boolean equals(Object obj) {
if(obj != null && obj instanceof Or)
{
return this.left.equals(((Or)obj).left) && this.right.equals(((Or)obj).right);
}
return false;
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
@Override
public boolean interpret(Context ctx) {
return left.interpret(ctx) || right.interpret(ctx);
}
@Override
public String toString() {
return "(" + left.toString() + " OR " + right.toString() + ")";
}
}
The Not class represents the logical "not" operation, which represents the operation of giving a new Boolean expression from a Boolean expression through the logical "not" operation. Copy the code as follows:
public class Not extends Expression {
private Expression exp;
public Not(Expression exp){
this.exp = exp;
}
@Override
public boolean equals(Object obj) {
if(obj != null && obj instanceof Not)
{
return exp.equals(
((Not)obj).exp);
}
return false;
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
@Override
public boolean interpret(Context ctx) {
return !exp.interpret(ctx);
}
@Override
public String toString() {
return "(Not " + exp.toString() + ")";
}
}
The Context class defines a mapping from variables to Boolean values
Copy the code code as follows:
public class Context {
private Map<Variable,Boolean> map = new HashMap<Variable,Boolean>();
public void assign(Variable var , boolean value){
map.put(var, new Boolean(value));
}
public boolean lookup(Variable var) throws IllegalArgumentException{
Boolean value = map.get(var);
if(value == null){
throw new IllegalArgumentException();
}
return value.booleanValue();
}
}
Client class
Copy the code code as follows:
public class Client {
public static void main(String[] args) {
Context ctx = new Context();
Variable x = new Variable("x");
Variable y = new Variable("y");
Constant c = new Constant(true);
ctx.assign(x, false);
ctx.assign(y, true);
Expression exp = new Or(new And(c,x) , new And(y,new Not(x)));
System.out.println("x=" + x.interpret(ctx));
System.out.println("y=" + y.interpret(ctx));
System.out.println(exp.toString() + "=" + exp.interpret(ctx));
}
}
The running results are as follows: