Click “like” to see, form a habit, the public account search [dime technology] pay attention to more original technical articles. This article has been included in GitHub org_Hejianhui /JavaStudy.

preface

  • 23 design modes for shorthand
  • The singleton pattern
  • Factory Method pattern
  • Abstract Factory pattern
  • The Builder/Builder pattern
  • Prototype mode
  • Flyweight mode
  • The facade pattern
  • Adapter mode
  • Decorator pattern
  • Observer mode
  • Strategy mode
  • Bridge mode
  • The Template Method pattern
  • The Chain of Responsibility model
  • Composite mode
  • Proxy mode
  • Memento mode
  • Command mode
  • State mode
  • Mediator mode
  • Iterator pattern
  • Updates continue at……

Here are 23 design patterns to memorize quicklyInterpreter modeRelated content.

The schema definition

Define a language for the parse object, define its grammatical representation, and design a parser to interpret sentences in the language. That is, analyze the application instances in the same way you compile the language. This pattern implements an interface for grammar expression processing that interprets a particular context.

The concepts of grammar and sentence mentioned here are the same as described in the compilation principle. “grammar” refers to the grammatical rules of the language, while “sentence” is an element of the language set. For example, there are many sentences in Chinese, “I am Chinese” is one of them, can be used a grammar tree to describe sentences in the language intuitively.

Structure and implementation of patterns

Interpreter mode is often used in the compilation or analysis of simple languages. In order to master its structure and implementation, we must first understand the concepts of “grammar, sentence, syntax tree” in the compilation principle.

grammar

A grammar is a formal rule that describes the grammatical structure of a language. Nothing can be accomplished without norms or standards, for example, some people think that perfect love, the rule is “attract each other, the sentiment is single-minded, neither party is love experience”, although the final rule is demanding, but everything has to have rules, language is the same, whether it’s a machine language and natural language, has its own rules of grammar. For example, the grammar of “sentence” in Chinese is as follows.

> < sentence: : = < subject > < predicate > < object > < subject > : : = < pronouns > | < nouns > < predicate > : : = > < verbs < object > : : = < pronouns > | < nouns > < pronouns > you | | I he < > noun7Japan's chardonnay I English students I < > verb: : = | learningCopy the code

Note: the symbol “::=” represents the meaning of “defined as”. Non-terminals enclosed by “<” and “>” are not enclosed by terminals.

The sentence

A sentence is the basic unit of language and an element of language set. It consists of terminators and can be deduced from grammar. For example, the above grammar can follow “I am a college student”, so it is a sentence.

The syntax tree

Grammar tree is a tree representation of sentence structure. It represents the result of derivation of a sentence. It is helpful to understand the hierarchy of sentence grammar structure. The grammar tree for “I am a college student” is shown below.

The structure of the interpreter pattern is similar to that of the composite pattern, but it contains more constituent elements than the composite pattern, and the composite pattern is an object structural pattern, while the interpreter pattern is a class behavior pattern.

Implementation of patterns

The key to the implementation of interpreter pattern is to define grammar rules, design terminal and non-terminal classes, draw a structure diagram, and build a syntax tree if necessary. The code structure is as follows:

package com.niuh.designpattern.interpreter.v1;

/** * 

* Interpreter mode *

*/
public class InterpreterPattern {}// Abstract expression class interface AbstractExpression { public Object interpret(String info); // Explain the method } // Terminator expression class class TerminalExpression implements AbstractExpression { public Object interpret(String info) { // Processing of terminal expressions return null; }}// Non-terminal expression class class NonterminalExpression implements AbstractExpression { private AbstractExpression exp1; private AbstractExpression exp2; public Object interpret(String info) { // Non-terminal expression processing return null; }}/ / environment class Context { private AbstractExpression exp; public Context(a) { // Data initialization } public void operation(String info) { // Call the interpretive method of the related expression class}}Copy the code

Problem solved

Build an interpreter that interprets sentences for some fixed grammar.

Patterns of

To compose (a role). role
Abstract Expression role Define the interface of the interpreter and specify the interpretation operations of the interpreter, mainly including the interpretation method interpret().
Terminal Expression Role Is a subclass of abstract expressions used to implement terminal-related operations in grammars. Each terminator in a grammar has a concrete terminating expression corresponding to it.
Nonterminal Expression role Also a subclass of abstract expressions, used to implement non-terminal related operations in grammars, where each rule corresponds to a non-terminal expression.
Context role Usually contains data required by individual interpreters or common functions used to pass data shared by all interpreters, from which subsequent interpreters can retrieve these values.
Client The main task is to convert the sentence or expression to be parsed into an abstract syntax tree described by the interpreter object, and then call the interpreter’s interpretive methods, which can also be accessed indirectly through the environment role.

Example is given to illustrate

Instance profiles

Using interpreter mode to design a Beijing bus card reader program.

Explanation: If the Beijing bus card reader can determine the identity of passengers, “old people”, “women” and “children” from “Haidian District” or “Chaoyang District” can ride for free, other passengers will be deducted 2 yuan per ride.

Analysis: this example with “interpreter mode” design is more suitable, first design its grammar rules as follows.

The < expression > : : = < city > < person > < city > : : = | chaoyang district, haidian district < person > : : | | = the old women childrenCopy the code

Then, according to the grammar rules according to the following steps to design the bus card reader program class diagram.

Using the step

Step 1: Define an abstract Expression interface that contains the interpret method (String info).

// Abstract expression class
interface Expression {
    public boolean interpret(String info);
}
Copy the code

Step 2: A Terminal Expression class is defined, which uses the Set class to store cities or people meeting the conditions, and implements the interpret(Stringinfo) method in the abstract Expression interface, which is used to determine whether the string to be analyzed is the Terminal in the Set.

class TerminalExpression implements Expression {
    private Set<String> set = new HashSet<String>();

    public TerminalExpression(String[] data) {
        for (int i = 0; i < data.length; i++) { set.add(data[i]); }}public boolean interpret(String info) {
        if (set.contains(info)) {
            return true;
        }
        return false; }}Copy the code

Step 3: Define a non-terminal expression (AndExpressicm) class, which is also a subclass of abstract expressions, that contains terminal expression objects for cities and people that meet the criteria, and implement the interpret(String info) method, Used to determine whether the string being analyzed is a qualified person in a city that meets the condition.

class AndExpression implements Expression {
    private Expression city = null;
    private Expression person = null;

    public AndExpression(Expression city, Expression person) {
        this.city = city;
        this.person = person;
    }

    public boolean interpret(String info) {
        String s[] = info.split("The");
        return city.interpret(s[0]) && person.interpret(s[1]); }}Copy the code

Step 4: Define a Context class that contains the data needed by the interpreter, completes the initialization of the terminator expression, and defines a method freeRide(String info) that calls the interpretation method of the expression object to interpret the parsed String.

class Context {
    private String[] citys = {Haidian District."Chaoyang District"};
    private String[] persons = {"Old man"."Women"."Children"};
    private Expression cityPerson;

    public Context(a) {
        Expression city = new TerminalExpression(citys);
        Expression person = new TerminalExpression(persons);
        cityPerson = new AndExpression(city, person);
    }

    public void freeRide(String info) {
        boolean ok = cityPerson.interpret(info);
        if (ok) {
            System.out.println("Are you" + info + ", you ride free!");
        } else {
            System.out.println(info + ", you are not free personnel, this ride buckle fee 2 yuan!"); }}}Copy the code

Step 5: Client test

public class InterpreterPattern {
    public static void main(String[] args) {
        Context bus = new Context();
        bus.freeRide("The Old man of Haidian District.");
        bus.freeRide("Young People in Haidian District.");
        bus.freeRide("Women in Chaoyang District.");
        bus.freeRide("Children in Chaoyang District");
        bus.freeRide("The Young Man of Nanjing"); }}Copy the code

The output

You are the old man of Haidian district, you ride free! Young people in Haidian district, you are not a free person, this ride deduction fee2Yuan! You are chaoyang district women, you ride free! You are chaoyang district children, you ride free! Nanjing young people, you are not free personnel, this bus deduction fee2Yuan!Copy the code

advantages

The interpreter pattern is a behavioral pattern with the following major advantages.

  1. Good scalability. Because classes are used in the interpreter schema to represent the grammar rules of the language, the grammar can be changed or extended through mechanisms such as inheritance.
  2. Easy to implement. Each expression node class in the syntax tree is similar, so the grammar is easier to implement.

disadvantages

  1. Execution efficiency is low. Interpreter mode usually uses a lot of loops and recursive calls, which are slow and cumbersome to debug when the sentences to be interpreted are complex.
  2. It causes class inflation. Each rule in the interpreter pattern needs to define at least one class. When there are many grammar rules, the number of classes increases dramatically, making it difficult to manage and maintain the system.
  3. There are fewer scenarios to apply. In software development, there are very few applications where language grammars need to be defined, so this pattern is rarely used.

Application scenarios

  1. When the grammar of the language is simple and execution efficiency is not the key issue.
  2. When the problem is repeated and can be expressed in a simple language.
  3. When a language requires interpretation execution, and the sentences in the language can be represented as an abstract syntax tree, as in XML document interpretation.

Schema extension

In project development, if you want to analyze and evaluate data expressions, you no longer need to use the interpreter pattern for design. Java provides the following powerful mathematical formula parser: Expression4J, MESP(Math Expression String Parser) and Jep, they can explain some complex grammar, powerful, simple to use.

Now use Jep as an example to illustrate how to use the toolkit. Jep, short for Java Expression Parser, is a Java library for converting and evaluating mathematical expressions. With this library, users can enter an arbitrary formula as a string and quickly calculate the result. Moreover, Jep supports user-defined variables, constants, and functions, including many commonly used mathematical functions and constants.

Configure dependency packages before use:

<! -- https://mvnrepository.com/artifact/jep/jep -->
<dependency>
    <groupId>jep</groupId>
    <artifactId>jep</artifactId>
    <version>2.24</version>
</dependency>
Copy the code

Here’s an example:

package com.niuh.designpattern.interpreter.v3;


import org.nfunk.jep.JEP;

/** * 

* JepDemo *

*/
public class JepDemo { public static void main(String[] args) { JEP jep = new JEP(); // a mathematical expression String exp = "((a+b)*(c+b))/(c+a)/b"; // Assign a value to a variable jep.addVariable("a".10); jep.addVariable("b".10); jep.addVariable("c".10); try { / / execution jep.parseExpression(exp); Object result = jep.getValueAsObject(); System.out.println("Calculation result:" + result); } catch (Throwable e) { System.out.println("An error occured: "+ e.getMessage()); }}}Copy the code

The running results of the program are as follows:

Calculation results:2.0
Copy the code

Application in source code

Application analysis of the interpreter pattern in SpelExpressionParser

The class diagram analysis

In the class diagram below, Expression is an interface that corresponds to a non-terminal Expression in our interpreter pattern, whereas ExpressionParser corresponds to a terminal Expression. Different Expression objects are returned depending on the Parser object.

Partial source code analysis

Expression interface

// Abstract non-terminal expression
public interface Expression {
	Object getValue(a) throws EvaluationException;
	
	Object getValue(Object rootObject) throws EvaluationException;
}
Copy the code

SpelExpression class

// A concrete non-terminal expression
public class SpelExpression implements Expression {
	@Override
	public Object getValue(a) throws EvaluationException {
		Object result;
		if (this.compiledAst ! =null) {
			try {
				TypedValue contextRoot = evaluationContext == null ? null : evaluationContext.getRootObject();
				return this.compiledAst.getValue(contextRoot == null ? null : contextRoot.getValue(), evaluationContext);
			}
			catch (Throwable ex) {
				// If running in mixed mode, revert to interpreted
				if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
					this.interpretedCount = 0;
					this.compiledAst = null;
				}
				else {
					// Running in SpelCompilerMode.immediate mode - propagate exception to caller
					throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
				}
			}
		}
		ExpressionState expressionState = new ExpressionState(getEvaluationContext(), this.configuration);
		result = this.ast.getValue(expressionState);
		checkCompile(expressionState);
		returnresult; }}Copy the code

CompositeStringExpression

// A concrete non-terminal expression
public class CompositeStringExpression implements Expression {
	@Override
	public String getValue(a) throws EvaluationException {
		StringBuilder sb = new StringBuilder();
		for (Expression expression : this.expressions) {
			String value = expression.getValue(String.class);
			if(value ! =null) { sb.append(value); }}returnsb.toString(); }}Copy the code

ExpressionParser interface

public interface ExpressionParser {
	// Parse the expression
	Expression parseExpression(String expressionString) throws ParseException;
	Expression parseExpression(String expressionString, ParserContext context) throws ParseException;
}
Copy the code

TemplateAwareExpressionParser class

public abstract class TemplateAwareExpressionParser implements ExpressionParser {
	@Override
	public Expression parseExpression(String expressionString) throws ParseException {
		return parseExpression(expressionString, NON_TEMPLATE_PARSER_CONTEXT);
	}
	// Returns a different Expression object depending on the parser
	@Override
	public Expression parseExpression(String expressionString, ParserContext context)
			throws ParseException {
		if (context == null) {
			context = NON_TEMPLATE_PARSER_CONTEXT;
		}
		if (context.isTemplate()) {
			return parseTemplate(expressionString, context);
		}
		else {
			returndoParseExpression(expressionString, context); }}private Expression parseTemplate(String expressionString, ParserContext context)
			throws ParseException {
		if (expressionString.length() == 0) {
			return new LiteralExpression("");
		}
		Expression[] expressions = parseExpressions(expressionString, context);
		if (expressions.length == 1) {
			return expressions[0];
		}
		else {
			return newCompositeStringExpression(expressionString, expressions); }}// Abstract, implemented by subclasses
	protected abstract Expression doParseExpression(String expressionString, ParserContext context) throws ParseException;
}
Copy the code

SpelExpressionParser class

public class SpelExpressionParser extends TemplateAwareExpressionParser {
	@Override
	protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {
		/ / return a InternalSpelExpressionParser here,
		return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context); }}Copy the code

InternalSpelExpressionParser class

class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
	@Override
	protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {
		try {
			this.expressionString = expressionString;
			Tokenizer tokenizer = new Tokenizer(expressionString);
			tokenizer.process();
			this.tokenStream = tokenizer.getTokens();
			this.tokenStreamLength = this.tokenStream.size();
			this.tokenStreamPointer = 0;
			this.constructedNodes.clear();
			SpelNodeImpl ast = eatExpression();
			if (moreTokens()) {
				throw new SpelParseException(peekToken().startPos, SpelMessage.MORE_INPUT, toString(nextToken()));
			}
			Assert.isTrue(this.constructedNodes.isEmpty());
			return new SpelExpression(expressionString, ast, this.configuration);
		}
		catch (InternalParseException ex) {
			throwex.getCause(); }}}Copy the code

PS: The above code is submitted to Github: github.com/Niuh-Study/…

GitHub Org_Hejianhui /JavaStudy GitHub Hejianhui /JavaStudy