개요
Interpreter 패턴에서는 프로그램으로 해결해야 하는 어떤 문제를 간단한 미니 프로그램으로 표현하고 미니 프로그램은 그 자체만으론 동작하지 않기 때문에 이를 동작하기 위해 Java 언어로 통역(Interprete)하는 통역 프로그램을 만든다. 여기서 이 통역 프로그램을 인터프리터라 부른다.
문제에 변경이 발생하면 인터프리터는 그대로 두고 미니 프로그램만을 수정해서 해결한다.
미니 프로그램을 작성하는 미니 언어는 단순한 명령들로 구성되는데 이 명령들을 조합해서 표현하는 것이 미니 언어라 보면 된다.
미니 프로그램은 미니 언어를 이용해서 작성하는데, 미니 언어에 포함되어 있는 명령들을 조합해서 프로그램을 작성한다. 미니 언어에도 문법이 존재하는데,
역할
AbstractExpression(추상 표현) 역할
구문 트리 노드의 공통 인터페이스를 결정한다. 예제에서는 Node 클래스가 parse라는 메소드로 공통 인터페이스를 제공.
TerminalExpression(종착점 표현) 역할
BNF의 Terminal Expression에 대응하는 역할로 예제에서는 PrimitiveCommandNode 클래스.
NonterminalExpression(비종착점 표현) 역할
BNF의 Nonterminal Expression에 대응하는 역할. 예제에서는 ProgramNode, CommandNode, RepeatCommandNode, CommandListNode 클래스.
Context(문맥, 전후 관계) 역할
인터프리터가 구문 해석을 실행하귀 위한 정보를 제공하는 역할. 예제에서 Context 클래스.
Client 역할
구문 트리를 조립하기 위해 TerminalExpression과 NonterminalExpression을 호출하는 역할. 예제에서는 Tester 클래스.
예제 코드
//AbstractExpression 역할
package interpreter;
public abstract class Node {
public abstract void parse(Context context) throws ParseException;
}
//TerminalExpression 역할
package interpreter;
public class PrimitiveCommandNode extends Node{
private String name;
public void parse(Context context) throws ParseException {
name = context.currentToekn();
context.skipToken(name);
if(!name.equals("go") && !name.equals("right") && !name.equals("left")) {
throw new ParseException(name + " is undefined");
}
}
public String toString() {
return name;
}
}
//NonterminalExpression 역할
package interpreter;
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 String toString() {
return "[program " + commandListNode + "]";
}
}
package interpreter;
public class CommandNode extends Node{
private Node node;
public void parse(Context context) throws ParseException {
if(context.currentToekn().equals("repeat")) {
node = new RepeatCommandNode();
node.parse(context);
} else {
node = new PrimitiveCommandNode();
node.parse(context);
}
}
public String toString() {
return node.toString();
}
}
package interpreter;
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 interpreter;
import java.util.ArrayList;
public class CommandListNode extends Node{
private ArrayList<Node> list = new ArrayList<>();
public void parse(Context context) throws ParseException {
while(true) {
if(context.currentToekn()==null) {
throw new ParseException("Missing 'end'");
} else if (context.currentToekn().equals("end")) {
context.skipToken("end");
break;
} else {
Node commandNode = new CommandNode();
commandNode.parse(context);
list.add(commandNode);
}
}
}
public String toString() {
return list.toString();
}
}
//Context 역할
package interpreter;
import java.util.StringTokenizer;
public class Context {
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 currentToekn() {
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("Waring : " + e);
}
return number;
}
}
//Client 역할
package interpreter;
import java.io.BufferedReader;
import java.io.FileReader;
public class InterpreterTester {
public static void main(String[] args) {
try {
BufferedReader reader = new BufferedReader(new FileReader("program.txt"));
String text;
while((text=reader.readLine()) != null) {
System.out.println("text = \"" + text + "\"");
Node node = new ProgramNode();
node.parse(new Context(text));
System.out.println("node = " + node);
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
'디자인 패턴' 카테고리의 다른 글
21. Proxy 패턴 (0) | 2021.01.11 |
---|---|
20. Flyweight 패턴 (0) | 2021.01.05 |
18. Memento 패턴 (0) | 2020.12.30 |
17. Observer 패턴 (0) | 2020.12.29 |
16. Mediator 패턴 (0) | 2020.12.28 |