事例紹介
a+b-c
計算値、特定の要件などの四則演算をインタプリタモードで実装
- 最初に式の形式を入力します。たとえば
a+b+c-d+e
、式の文字を繰り返すことができないようにする必要があります。 - 別途入力した
a,b,c,d,e
値 - 最後に結果を見つけます
従来のソリューション
- 式の形式を受け取るメソッドを作成し、ユーザーが入力した値に基づいてそれを解析して結果を取得します。
【分析する】
* や / などの新しい演算子を追加すると拡張に役立ちませんし、1 つのメソッドで解析できるようにするとプログラム構造が混乱してわかりにくくなります。
【改善する】
インタプリタモードの使用を検討してください。表达式->解释器(可以有多种解释器)->结果
導入
基本的な紹介
- インタプリタモードでは、プログラムが解決すべき問題は非常に単純な「ミニ言語」で表現されます。つまり、「ミニ言語」で書かれたミニプログラムが特定の問題を表現します。ミニプログラムだけでは動作しないので、「インタプリタ」を担うプログラムもJava言語で書く必要があります。翻訳者はミニ言語を理解し、解釈し、最後にミニプログラムを実行します。この翻訳プログラムはインタプリタとも呼ばれます。これにより、解決すべき問題が変化した場合でも、Java言語プログラムを変更する必要はなく、ミニ言語プログラムのみを変更するだけで対応できる。
- コンパイル原理では、算術式は字句解析器によって字句単位に形成され、次にこれらの字句単位が構文解析器によって構築されて構文解析ツリーが構築され、最後に抽象構文解析ツリーが形成されます。ここでの字句アナライザーと構文アナライザーは両方ともインタープリターとみなすことができます。
アプリケーションシナリオ
- 解釈して実行する必要がある言語の文は、抽象構文ツリーとして表現できます。コンパイラ、演算式の計算、正規表現、ロボットの命令など、繰り返し発生する問題の一部は、単純な言語で表現できます。 …
キャラクター
AbstractExpression(抽象表达式)
: 抽象解釈操作を宣言する抽象式 (構文ツリー ノードの共通インターフェイスを定義)。このメソッドは、抽象構文ツリー内のすべてのノードで共有されます。このメソッドには、解析/変換として翻訳される、解析/インタープリターという名前を付けることができます。TerminalExpression(终结符表达式)
: これは終端表現であり、文法内の終端記号に関連する解釈操作を実装します。NonTermialExpression(非终结符表达式)
: これは非終端式であり、文法内の非終端記号の解釈操作を実装します。Context(上下文)
: これは、インタプリタの外部にあるグローバル情報を含み、インタプリタが文法解析を実行するために必要な情報を提供する環境ロールです。Client(请求者)
: TerminalExpression と NonTermialExpression を呼び出して構文ツリーを派生します
事例の実装
ケースその1
クラス図
成し遂げる
【表現】
package com.atguigu.interpreter;
import java.util.HashMap;
/**
* 抽象类表达式,通过HashMap键值对, 可以获取到变量的值
*
* @author Administrator
*
*/
public abstract class Expression {
/**
* 如表达式是:a + b - c ,key就是公式(表达式)的参数a、b、c, value就是就是具体值
* 实例:HashMap {a=10, b=20}
* @param var
* @return
*/
public abstract int interpreter(HashMap<String, Integer> var);
}
【変数パーサー】
package com.atguigu.interpreter;
import java.util.HashMap;
/**
* 变量的解释器
* @author Administrator
*
*/
public class VarExpression extends Expression {
/**
* key=a,key=b,key=c
*/
private String key;
public VarExpression(String key) {
this.key = key;
}
/**
* var 就是{a=10, b=20}
* interpreter的功能就是根据变量名称来返回对应值
* @param var
* @return
*/
@Override
public int interpreter(HashMap<String, Integer> var) {
return var.get(this.key);
}
}
【抽象算術記号インタプリタ】
package com.atguigu.interpreter;
import java.util.HashMap;
/**
* 抽象运算符号解析器
* 每个运算符号,都只和自己左右两个数字有关系,
* 但左右两个数字有可能也是一个解析的结果,无论何种类型,都是Expression类的实现类
*
* @author Administrator
*
*/
public class SymbolExpression extends Expression {
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
/**
* 因为 SymbolExpression 是让其子类来实现,因此 interpreter 是一个默认实现
* @param var
* @return
*/
@Override
public int interpreter(HashMap<String, Integer> var) {
// 默认实现
return 0;
}
}
【特定演算記号インタプリタ:加算インタプリタ】
package com.atguigu.interpreter;
import java.util.HashMap;
/**
* 加法解释器
* @author Administrator
*
*/
public class AddExpression extends SymbolExpression {
public AddExpression(Expression left, Expression right) {
super(left, right);
}
/**
* 处理相加
* var 仍然是 {a=10,b=20}..
* @param var
* @return
*/
public int interpreter(HashMap<String, Integer> var) {
// super.left.interpreter(var):返回 left 表达式对应的值 a = 10
// super.right.interpreter(var): 返回 right 表达式对应值 b = 20
// 将运算左表达式的值和右表达式相加
return super.left.interpreter(var) + super.right.interpreter(var);
}
}
【特定演算記号解釈部:減算解釈部】
package com.atguigu.interpreter;
import java.util.HashMap;
/**
* 减法解释器
*/
public class SubExpression extends SymbolExpression {
public SubExpression(Expression left, Expression right) {
super(left, right);
}
/**
* 求出left 和 right 表达式相减后的结果
*
* @param var
* @return
*/
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) - super.right.interpreter(var);
}
}
【電卓】
package com.atguigu.interpreter;
import java.util.HashMap;
import java.util.Stack;
public class Calculator {
/**
* 定义表达式
*/
private Expression expression;
/**
* 构造函数传参,解析字符串生成表达式
* @param expStr
*/
public Calculator(String expStr) {
// 如 expStr = a+b
// 安排运算先后顺序
Stack<Expression> stack = new Stack<>();
// 表达式拆分成字符数组,变成[a, +, b]
char[] charArray = expStr.toCharArray();
Expression left = null;
Expression right = null;
//遍历我们的字符数组, 即遍历 [a, +, b]
//针对不同的情况,做处理
for (int i = 0; i < charArray.length; i++) {
switch (charArray[i]) {
case '+':
// 从stack取出左表达式 "a"
left = stack.pop();
// 取出右表达式 "b"
right = new VarExpression(String.valueOf(charArray[++i]));
// 然后根据得到left和right构建AddExpresson加入stack
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:
//如果是一个 Var 就创建要给 VarExpression 对象,并push到stack
stack.push(new VarExpression(String.valueOf(charArray[i])));
break;
}
}
//当遍历完整个charArray数组后,stack就得到最终的Expression
this.expression = stack.pop();
}
public int run(HashMap<String, Integer> var) {
//最后将表达式 a+b 和 var={a=10,b=20}
//然后传递给expression的interpreter进行解释执行
return this.expression.interpreter(var);
}
}
【クライアント】
package com.atguigu.interpreter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
public class ClientTest {
public static void main(String[] args) throws IOException {
// a+b
String expStr = getExpStr();
// var {a=10, b=20}
HashMap<String, Integer> var = getValue(expStr);
Calculator calculator = new Calculator(expStr);
System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
}
/**
* 获得表达式
*
* @return
* @throws IOException
*/
public static String getExpStr() throws IOException {
System.out.print("请输入表达式:");
return (new BufferedReader(new InputStreamReader(System.in))).readLine();
}
/**
* 获得值映射
*
* @param expStr
* @return
* @throws IOException
*/
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("请输入" + String.valueOf(ch) + "的值:");
String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
map.put(String.valueOf(ch), Integer.valueOf(in));
}
}
}
return map;
}
}
【走る】
Connected to the target VM, address: '127.0.0.1:4322', transport: 'socket'
请输入表达式:a+b
请输入a的值:10
请输入b的值:20
运算结果:a+b=30
Disconnected from the target VM, address: '127.0.0.1:4322', transport: 'socket'
Process finished with exit code 0
【実施プロセス】
- 最初のループ: 変数パーサー a をスタックに置きます
- 2 番目のループ: スタックから左側の式 "a" を取得し、配列から新しい式 "b" を取得して生成し、最後に加算式 "a+b" を構築してスタックに格納します。
ケース2
説明する
車があり、車の動きを制御する簡単な小さなプログラムを書く必要があります。たとえばprogram go right go right go right go right end
、車が指示を受け取ると、次のような軌道をたどります。
クラス図
成し遂げる
[抽象式:ノード]
package com.atguigu.interpreter.Sample;
/**
* 语法树中各个部分(节点)中最顶层的类
*/
public abstract class Node {
/**
* 进行语法解析处理
*
* @param context 语法解析上下文的类
* @throws ParseException
*/
public abstract void parse(Context context) throws ParseException;
}
[カスタマイズされた解析例外]
package com.atguigu.interpreter.Sample;
public class ParseException extends Exception {
public ParseException(String msg) {
super(msg);
}
}
[ターミネータ式:PrimitiveCommandNode]
終端式: それ以上の展開は続行されず、parse メソッドが呼び出されます。
package com.atguigu.interpreter.Sample;
// <primitive command> ::= go | right | left
public class PrimitiveCommandNode extends Node {
/**
* 记录 指令的名字 如 go left right
*/
private String name;
/**
* PrimitiveCommandNode 的 parse 方法没有调用其他类的parse方法
* @param context 语法解析上下文的类
* @throws ParseException
*/
public void parse(Context context) throws ParseException {
// 记录指令的名字
name = context.currentToken();
context.skipToken(name);
if (!name.equals("go") && !name.equals("right") && !name.equals("left")) {
throw new ParseException(name + " is undefined");
}
}
public String toString() {
return name;
}
}
[非終端式:ProgramNode]
package com.atguigu.interpreter.Sample;
// <program> ::= program <command list>
public class ProgramNode extends Node {
private Node commandListNode;
public void parse(Context context) throws ParseException {
// 迷你语法最开始会出现单词program,这行代码可以跳过 program 这个标记
// 比如一开始context的值是program end,那么currentToken的值就是program,执行context.skipToken("program")后,currentToken的值变成end
context.skipToken("program");
commandListNode = new CommandListNode();
commandListNode.parse(context);
}
public String toString() {
// 等效于 return "[program " + commandListNode.toString() + "]";
return "[program " + commandListNode + "]";
}
}
[非終端式: CommandNode]
package com.atguigu.interpreter.Sample;
// <command> ::= <repeat command> | <primitive command>
public class CommandNode extends Node {
private Node node;
public void parse(Context context) throws ParseException {
if (context.currentToken().equals("repeat")) {
// 使用repeat解析器
node = new RepeatCommandNode();
node.parse(context);
} else {
// 使用指令解释器,解析go left right等指令
node = new PrimitiveCommandNode();
node.parse(context);
}
}
public String toString() {
return node.toString();
}
}
[非終端式: CommandListNode]
package com.atguigu.interpreter.Sample;
import java.util.ArrayList;
// <command list> ::= <command>* end
public class CommandListNode extends Node {
/**
* 保存多个命令
*/
private ArrayList list = new ArrayList();
public void parse(Context context) throws ParseException {
while (true) {
if (context.currentToken() == null) {
// 如果context.currentToken() == null,表示后面没有任何标记了(即已经解析到迷你程序的末尾),说明缺少了end,抛出异常
throw new ParseException("Missing 'end'");
} else if (context.currentToken().equals("end")) {
// 如果当前的标记是end,表示已经解析至末尾,end不需要执行,直接跳过即可
context.skipToken("end");
// 到了end,解析已经完成了,退出循环即可
break;
} else {
// 当前标记不是end,则是其他需要解析的标记
Node commandNode = new CommandNode();
// 解析标记
commandNode.parse(context);
list.add(commandNode);
}
}
}
public String toString() {
return list.toString();
}
}
[非終端式:RepeatCommandNode]
package com.atguigu.interpreter.Sample;
// <repeat command> ::= repeat <number> <command list>
public class RepeatCommandNode extends Node {
/**
* 循环调用的次数
*/
private int number;
private Node commandListNode;
public void parse(Context context) throws ParseException {
context.skipToken("repeat");
number = context.currentNumber();
context.nextToken();
commandListNode = new CommandListNode();
commandListNode.parse(context);
}
public String toString() {
return "[repeat " + number + " " + commandListNode + "]";
}
}
【コンテクスト】
package com.atguigu.interpreter.Sample;
import java.util.StringTokenizer;
/**
* 该类提供语法解析需要的方法
*/
public class Context {
/**
* 使用java.util.stringTokenizer类来简化程序,它会将接收到的字符串分割为标记。
* 在分割字符串时使用的分隔符是空格“”、制表符“\t”、换行符“\n”回车符“\r”、换页符“\f”
*/
private StringTokenizer tokenizer;
private String currentToken;
public Context(String text) {
tokenizer = new StringTokenizer(text);
nextToken();
}
/**
* 获取下一个标记
*
* @return
*/
public String nextToken() {
// 当判断还有下一个标记时,就获取下一个标记
if (tokenizer.hasMoreTokens()) {
currentToken = tokenizer.nextToken();
} else {
currentToken = null;
}
return currentToken;
}
/**
* 返回当前的标记
* @return
*/
public String currentToken() {
return currentToken;
}
/**
* 跳过标记
*
* @param token
* @throws ParseException
*/
public void skipToken(String token) throws ParseException {
if (!token.equals(currentToken)) {
throw new ParseException("Warning: " + token + " is expected, but " + currentToken + " is found.");
}
nextToken();
}
/**
* 读取数字
*
* @return
* @throws ParseException
*/
public int currentNumber() throws ParseException {
int number = 0;
try {
number = Integer.parseInt(currentToken);
} catch (NumberFormatException e) {
throw new ParseException("Warning: " + e);
}
return number;
}
}
【クライアント:メイン】
package com.atguigu.interpreter.Sample;
import java.io.BufferedReader;
import java.io.FileReader;
public class Main {
public static void main(String[] args) {
try {
BufferedReader reader = new BufferedReader(new FileReader("src/com/atguigu/interpreter/Sample/program.txt"));
String text;
while ((text = reader.readLine()) != null) {
System.out.println("迷你程序 = \"" + text + "\"");
Node node = new ProgramNode();
node.parse(new Context(text));
System.out.println("语法解析结果 = " + node);
System.out.println();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
【プログラム.txt】
program end
program go end
program go right go right go right go right end
program repeat 4 go right end end
program repeat 4 repeat 3 go right go left end right end end
【走る】
迷你程序 = "program end"
语法解析结果 = [program []]
迷你程序 = "program go end"
语法解析结果 = [program [go]]
迷你程序 = "program go right go right go right go right end"
语法解析结果 = [program [go, right, go, right, go, right, go, right]]
迷你程序 = "program repeat 4 go right end end"
语法解析结果 = [program [[repeat 4 [go, right]]]]
迷你程序 = "program repeat 4 repeat 3 go right go left end right end end"
语法解析结果 = [program [[repeat 4 [[repeat 3 [go, right, go, left]], right]]]]
Process finished with exit code 0
拡大する
- 上記のプログラムはミニプログラムを解析するだけで実際に命令を実行するわけではありませんが、実際に車が命令通りに実行できるように改良を進めていきます。
- 次のコードは確かに少し複雑です。このコードはインタプリタ モードを使用するだけでなく、アピアランス モードを使用してインタプリタを使いやすくしています。また、ファクトリ メソッド モードを使用して、対応する実行を生成するメソッドを提供します。命令名まで、
createExecutor(String name)
じっくりとご鑑賞ください。
【解析例外】
package com.atguigu.interpreter.A1.language;
public class ParseException extends Exception {
public ParseException(String msg) {
super(msg);
}
}
【例外の実行】
package com.atguigu.interpreter.A1.language;
public class ExecuteException extends Exception {
public ExecuteException(String msg) {
super(msg);
}
}
【ノード】
package com.atguigu.interpreter.A1.language;
/**
* 实现Executor执行器接口
*/
public abstract class Node implements Executor {
public abstract void parse(Context context) throws ParseException;
}
【プログラムノード】
package com.atguigu.interpreter.A1.language;
public class ProgramNode extends Node {
private Node commandListNode;
public void parse(Context context) throws ParseException {
context.skipToken("program");
commandListNode = new CommandListNode();
commandListNode.parse(context);
}
public void execute() throws ExecuteException {
// 连续执行多个指令 的 execute方法
commandListNode.execute();
}
public String toString() {
return "[program " + commandListNode + "]";
}
}
【コマンドノード】
package com.atguigu.interpreter.A1.language;
public class CommandNode extends Node {
private Node node;
public void parse(Context context) throws ParseException {
if (context.currentToken().equals("repeat")) {
node = new RepeatCommandNode();
node.parse(context);
} else {
node = new PrimitiveCommandNode();
node.parse(context);
}
}
/**
* 直接调用 RepeatCommandNode 和 PrimitiveCommandNode 的执行器
* @throws ExecuteException
*/
public void execute() throws ExecuteException {
node.execute();
}
public String toString() {
return node.toString();
}
}
【コマンドリストノード】
package com.atguigu.interpreter.A1.language;
import java.util.ArrayList;
import java.util.Iterator;
public class CommandListNode extends Node {
private ArrayList list = new ArrayList();
public void parse(Context context) throws ParseException {
while (true) {
if (context.currentToken() == null) {
throw new ParseException("Missing 'end'");
} else if (context.currentToken().equals("end")) {
context.skipToken("end");
break;
} else {
Node commandNode = new CommandNode();
commandNode.parse(context);
list.add(commandNode);
}
}
}
/**
* 使用迭代器来自动执行指令
*
* @throws ExecuteException
*/
public void execute() throws ExecuteException {
Iterator it = list.iterator();
while (it.hasNext()) {
((CommandNode) it.next()).execute();
}
}
public String toString() {
return list.toString();
}
}
【プリミティブコマンドノード】
package com.atguigu.interpreter.A1.language;
public class PrimitiveCommandNode extends Node {
private String name;
private Executor executor;
public void parse(Context context) throws ParseException {
name = context.currentToken();
context.skipToken(name);
// 根据指令名称来找工厂获取相应的执行器
executor = context.createExecutor(name);
}
public void execute() throws ExecuteException {
if (executor == null) {
throw new ExecuteException(name + ": is not defined");
} else {
executor.execute();
}
}
public String toString() {
return name;
}
}
【RepeatCommandNode】
package com.atguigu.interpreter.A1.language;
public class RepeatCommandNode extends Node {
private int number;
private Node commandListNode;
public void parse(Context context) throws ParseException {
context.skipToken("repeat");
number = context.currentNumber();
context.nextToken();
commandListNode = new CommandListNode();
commandListNode.parse(context);
}
public void execute() throws ExecuteException {
// 循环执行指令
for (int i = 0; i < number; i++) {
commandListNode.execute();
}
}
public String toString() {
return "[repeat " + number + " " + commandListNode + "]";
}
}
【コンテクスト】
package com.atguigu.interpreter.A1.language;
import java.util.StringTokenizer;
public class Context implements ExecutorFactory {
/**
* 组合工厂类
*/
private ExecutorFactory factory;
private StringTokenizer tokenizer;
private String currentToken;
public Context(String text) {
tokenizer = new StringTokenizer(text);
nextToken();
}
public String nextToken() {
if (tokenizer.hasMoreTokens()) {
currentToken = tokenizer.nextToken();
} else {
currentToken = null;
}
return currentToken;
}
public String currentToken() {
return currentToken;
}
public void skipToken(String token) throws ParseException {
if (!token.equals(currentToken)) {
throw new ParseException("Warning: " + token + " is expected, but " + currentToken + " is found.");
}
nextToken();
}
public int currentNumber() throws ParseException {
int number = 0;
try {
number = Integer.parseInt(currentToken);
} catch (NumberFormatException e) {
throw new ParseException("Warning: " + e);
}
return number;
}
/**
* 设置工厂
* @param factory
*/
public void setExecutorFactory(ExecutorFactory factory) {
this.factory = factory;
}
/**
* 使用工厂的方法来创建具体的执行器
* @param name
* @return
*/
public Executor createExecutor(String name) {
// 后面的终结符
return factory.createExecutor(name);
}
}
【執行者】
package com.atguigu.interpreter.A1.language;
/**
* 外观对象的窗口接口
*/
public interface Executor {
/**
* 向系统外部提供一个接口
* @throws ExecuteException
*/
public abstract void execute() throws ExecuteException;
}
【通訳ファサード】
package com.atguigu.interpreter.A1.language;
public class InterpreterFacade implements Executor {
private ExecutorFactory factory;
private Context context;
private Node programNode;
public InterpreterFacade(ExecutorFactory factory) {
this.factory = factory;
}
/**
* 提供给外层访问的解析接口
* @param text
* @return
*/
public boolean parse(String text) {
boolean ok = true;
this.context = new Context(text);
this.context.setExecutorFactory(factory);
this.programNode = new ProgramNode();
try {
// 开始解析
programNode.parse(context);
System.out.println(programNode.toString());
} catch (ParseException e) {
e.printStackTrace();
ok = false;
}
return ok;
}
public void execute() throws ExecuteException {
try {
// 开始执行程序
programNode.execute();
} catch (ExecuteException e) {
e.printStackTrace();
}
}
}
【エグゼキューターファクトリー】
package com.atguigu.interpreter.A1.language;
public interface ExecutorFactory {
/**
* 创建一个执行器
* @param name
* @return
*/
public abstract Executor createExecutor(String name);
}
【タートルキャンバス】
package com.atguigu.interpreter.A1.turtle;
import com.atguigu.interpreter.A1.language.ExecuteException;
import com.atguigu.interpreter.A1.language.Executor;
import com.atguigu.interpreter.A1.language.ExecutorFactory;
import java.awt.*;
public class TurtleCanvas extends Canvas implements ExecutorFactory {
/**
* 前进时的长度单位
*/
final static int UNIT_LENGTH = 30;
/**
* 上方
*/
final static int DIRECTION_UP = 0;
/**
* 右方
*/
final static int DIRECTION_RIGHT = 3;
/**
* 下方
*/
final static int DIRECTION_DOWN = 6;
/**
* 左方
*/
final static int DIRECTION_LEFT = 9;
/**
* 右转
*/
final static int RELATIVE_DIRECTION_RIGHT = 3;
/**
* 左转
*/
final static int RELATIVE_DIRECTION_LEFT = -3;
/**
* 半径
*/
final static int RADIUS = 3;
/**
* 移动方向
*/
private int direction = 0;
/**
* 小车的定位
*/
private Point position;
private Executor executor;
public TurtleCanvas(int width, int height) {
// 设置画布尺寸
setSize(width, height);
initialize();
}
public void setExecutor(Executor executor) {
this.executor = executor;
}
/**
* 修改小车的行驶方向
*
* @param relativeDirection
*/
void setRelativeDirection(int relativeDirection) {
setDirection(direction + relativeDirection);
}
void setDirection(int direction) {
if (direction < 0) {
direction = 12 - (-direction) % 12;
} else {
direction = direction % 12;
}
this.direction = direction % 12;
}
/**
* 让小车移动
*
* @param length
*/
void go(int length) {
int newx = position.x;
int newy = position.y;
switch (direction) {
case DIRECTION_UP:
newy -= length;
break;
case DIRECTION_RIGHT:
newx += length;
break;
case DIRECTION_DOWN:
newy += length;
break;
case DIRECTION_LEFT:
newx -= length;
break;
default:
break;
}
Graphics g = getGraphics();
if (g != null) {
g.drawLine(position.x, position.y, newx, newy);
g.fillOval(newx - RADIUS, newy - RADIUS, RADIUS * 2 + 1, RADIUS * 2 + 1);
}
position.x = newx;
position.y = newy;
}
/**
* 使用工厂模式根据指令名称创建一个对应的执行器,并将其赋值给Executor
*
* @param name
* @return
*/
public Executor createExecutor(String name) {
if (name.equals("go")) {
return new GoExecutor(this);
} else if (name.equals("right")) {
return new DirectionExecutor(this, RELATIVE_DIRECTION_RIGHT);
} else if (name.equals("left")) {
return new DirectionExecutor(this, RELATIVE_DIRECTION_LEFT);
} else {
return null;
}
}
/**
* 初始化
*/
public void initialize() {
Dimension size = getSize();
// 将小车的初始位置放在画布的中心
position = new Point(size.width / 2, size.height / 2);
direction = 0;
// 设置路径的颜色
setForeground(Color.red);
// 设置画布的背景颜色
setBackground(Color.white);
Graphics g = getGraphics();
if (g != null) {
// 清空画布
g.clearRect(0, 0, size.width, size.height);
}
}
/**
* 绘制图像
*
* @param g the specified Graphics context
*/
public void paint(Graphics g) {
initialize();
if (executor != null) {
try {
// 执行 执行器的方法 控制小车运动
executor.execute();
} catch (ExecuteException e) {
}
}
}
}
abstract class TurtleExecutor implements Executor {
protected TurtleCanvas canvas;
public TurtleExecutor(TurtleCanvas canvas) {
this.canvas = canvas;
}
public abstract void execute();
}
/**
* 具体执行器:前进
*/
class GoExecutor extends TurtleExecutor {
public GoExecutor(TurtleCanvas canvas) {
super(canvas);
}
public void execute() {
// 调用前进方法在画布中绘制小车的前进路径
canvas.go(TurtleCanvas.UNIT_LENGTH);
}
}
/**
* 具体执行器:切换方向
*/
class DirectionExecutor extends TurtleExecutor {
private int relativeDirection;
public DirectionExecutor(TurtleCanvas canvas, int relativeDirection) {
super(canvas);
this.relativeDirection = relativeDirection;
}
public void execute() {
// 修改小车的方向
canvas.setRelativeDirection(relativeDirection);
}
}
【主要】
package com.atguigu.interpreter.A1;
import com.atguigu.interpreter.A1.language.InterpreterFacade;
import com.atguigu.interpreter.A1.turtle.TurtleCanvas;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class Main extends Frame implements ActionListener {
private TurtleCanvas canvas = new TurtleCanvas(400, 400);
private InterpreterFacade facade = new InterpreterFacade(canvas);
/**
* 默认的迷你程序
*/
private TextField programTextField = new TextField("program repeat 3 go right go left end end");
/**
* 构造函数
*
* @param title
*/
public Main(String title) {
super(title);
canvas.setExecutor(facade);
setLayout(new BorderLayout());
programTextField.addActionListener(this);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
// 将文本输入框添加到布局的上部分
add(programTextField, BorderLayout.NORTH);
// 将画布放在布局的中心
add(canvas, BorderLayout.CENTER);
pack();
parseAndExecute();
show();
}
/**
* 供ActionListener用,监听用户的输入,当用户输入完成并按下回车之后,方法被执行
*
* @param e
*/
public void actionPerformed(ActionEvent e) {
if (e.getSource() == programTextField) {
parseAndExecute();
}
}
/**
* 解析迷你程序成指令,并执行指令
*/
private void parseAndExecute() {
// 获取用户输入的迷你程序
String programText = programTextField.getText();
System.out.println("programText = " + programText);
// 直接调用外观对象所提供的上层接口来使用解释器模式来解析迷你程序
facade.parse(programText);
// 重新绘制结果
canvas.repaint();
}
public static void main(String[] args) {
new Main("Interpreter Pattern Sample");
}
}
【走る】
Springフレームワークでのインタープリタパターンの適用
package com.atguigu.spring.test;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class Interpreter {
public static void main(String[] args) {
//创建一个 Parser 对象
SpelExpressionParser parser = new SpelExpressionParser();
//通过 Parser 对象 获取到一个Expression对象
//会根据不同的 Parser 对象 ,返回不同的 Expression 对象
Expression expression = parser.parseExpression("10 * (2 + 1) * 1 + 66"); //结果:96
int result = (Integer) expression.getValue();
System.out.println(result);
}
}
式のサブクラス
【イラスト】
- Expression インターフェイスは式インターフェイスであり、その下に SpelExpression や CompositeStringExpression などのさまざまな実装クラスがあります。
- 使用すると、作成したさまざまな Parser オブジェクトに基づいてさまざまな Expression オブジェクトが返されます。
- 最後に、取得した Expression オブジェクトを使用して getValue を呼び出し、式を解釈して実行し、結果を取得します。
要約する
【アドバンテージ】
- インタプリタと実行が必要な言語がある場合、その言語の文を抽象構文ツリーとして表現できるため、スケーラビリティの高いプログラムにするためにインタプリタモードの使用を検討できます。
【欠点】
- インタプリタ モードではクラス拡張が発生し、再帰呼び出しメソッドを使用するため、デバッグが非常に複雑になり、効率が低下する可能性があります。
記事の説明
- この記事は、シャン シリコン バレーを勉強するための私の学習ノートです。記事のほとんどの内容は、シャン シリコン バレーのビデオ (クリックしてシャン シリコン バレー関連コースを学習する) から引用していますが、一部の内容は私自身の考えによるものです。他の人がより便利に勉強できるように記事を作成します。自分のメモを整理したり、記事を通じて関連する知識を直接学習したりできます。侵害がある場合は、削除するためにご連絡ください。最後に、Shang Silicon Valley に感謝の意を表したいと思います。質の高いコース。
- また、『グラフィックデザインパターン』(グラフィックデザインパターン/結城宏著、楊文宣訳、北京:人民郵政通信社、2017.1)という書籍も同時に読み、両者の内容を統合して知識を作りました。ポイントをより包括的に。