Problem:
In object-oriented design and development, it is often encountered that some requests or operations are difficult to express or process in the form of objects, for example, if we write a simple arithmetic computing tool to calculate "A + B", we can simply define a method, receive two variables, and perform arithmetic "+" to calculate the returned results, however, if we allow this method to implement the four arithmetic operations of "addition, subtraction, multiplication, division", we need to modify the method and add an operator parameter. However, due to the change in requirements, we need to add operations of multiple operands, such: "A + B-C * D", what should I do? It is obviously impossible to define all possible operations using the exhaustive method. Can we define a method to parse arithmetic expressions and pass them directly as strings to the calculation method, the calculation method parses the arithmetic expression before calculating the returned result?
Definition:
The interpreter mode is a behavior mode. Given a language, it defines a syntax expression and defines an interpreter. This interpreter uses this expression to explain sentences in the language.
Intent:
Express complicated problems in a specific field as sentences under certain grammar rules, and then build an interpreter to explain such sentences, to deal with the problem of frequent changes in the use of common programming methods. (Create a syntax tree and use the syntax to parse the expression .)
Participants:
• Abstract expression role: Declares the abstract interface that all specific expression roles need to implement. This interface is mainly an interpret () method, which is called an interpreted operation.
• Terminal expression role: implements the interface required by the abstract expression role, mainly an interpret () method; each terminator in grammar corresponds to a specific Terminator expression. For example, there is a simple formula r = R1 + R2 in which R1 and R2 are the Terminator, and the interpreter for parsing R1 and R2 is the terminator expression.
• Nonterminal expression: each rule in the grammar requires a specific non-terminator expression. Non-terminator expressions are generally operators or other keywords in the grammar, for example, in formula r = R1 + R2, "+" is a non-Terminator, and the interpreter for parsing "+" is a non-terminator expression.
• Context role: the task of this role is generally used to store the specific values corresponding to the terminator in the grammar. For example, r = R1 + R2. we assign a value of 100 to R1, assign a value of 200 to R2. This information needs to be stored in the Environment role. In many cases, it is enough to use map to act as the Environment role.
UML:
CodeNote:
An example of parsing arithmetic expression computation. to quickly illustrate the problem, this example only supports double-operand computation.
/// <Summary>
/// Abstract Expression role
/// </Summary>
Public Abstract Class Expression
{
Public Abstract Void Interpret ();
}
/// <Summary>
/// Specific arithmetic parser expression
/// </Summary>
Public Class Calculatorexpression: Expression
{
Public Int Value;
String Expression;
Public Calculatorexpression ( String _ Expression)
{
Expression = _ expression;
}
Public Override Void Interpret ()
{
String [] Numbers = expression. Split ( ' + ' , ' - ' );
If (Expression. indexof ( ' + ' )>- 1 )
{
This . Value =Int . Parse (numbers [ 0 ]) + Int . Parse (numbers [ 1 ]);
}
Else If (Expression. indexof ( ' - ' )>- 1 )
{
This . Value = Int . Parse (numbers [ 0 ])- Int . Parse (numbers [ 1 ]);
}
Else If (Expression. indexof (' * ' )>- 1 )
{
This . Value = Int . Parse (numbers [ 0 ]) * Int . Parse (numbers [ 1 ]);
}
Else If (Expression. indexof ( ' / ' )>- 1 )
{
This . Value = Int . Parse (numbers [0 ])/ Int . Parse (numbers [ 1 ]);
}
}
}
/// <Summary>
/// Environment (context) Role
/// </Summary>
Public Class Contextcalculator
{
/// <Summary>
/// Calculation Method
/// </Summary>
/// <Param name = "expression"> </param>
/// <Returns> </returns>
Public Int Calculate ( String Expression)
{
Calculatorexpression EX = New Calculatorexpression (expression );
Ex. interpret ();
Return Ex. value;
}
}
Public Void Interpretertest ()
{
Contextcalculator c = New Contextcalculator ();
// Input expression Calculation
// No matter whether it is extended to multiple operands or adding parentheses, the client will not be affected.
C. Calculate ( " 2 + 1 " );
}
Advantages:
• It is easy to change and extend grammar because the pattern uses classes to represent grammar rules and you can use inheritance to change or extend grammar for flexible extension.
• Easy syntax implementation, because the implementation of classes on each node in the definition abstract syntax tree is similar in general, and these classes are easy to be directly written.
Disadvantages:
• Each rule in grammar defines at least one class. For complex syntax expressions, a large class hierarchy is generated, which is difficult to manage and maintain.
• Because the sentence will be analyzed into a tree structure and the interpreter needs to access it recursively, the efficiency will be affected.
Application scenarios:
If a specific type of problem occurs frequently enough, it may be worthwhile to express each instance of the problem as a sentence in a simple language. In this way, an interpreter can be built to solve the problem by interpreting these sentences.
PS:The. NET System provides many interpreters, such as: LINQ and regular expressions.