언어의 다양한 해석 구체적으로 구문을 나누고 그 분리된 구문의 해석을 맡는 클래스를 각각 작성하여 여러형태의 언어 구문을 해석할 수 있게 만드는 패턴이다.
- AbstractExpression(추상적인 표현): 구문 트리의 노드에 공통의 인터페이스(API)를 결정하는 역할
- terminalExpression(종착점 표현): BNF의 Terminal Expression에 대응하는 역할
- NonterminalExpression(비종착점 표현): BNF의 Nonterminal Expression에 대응하는 역할
- Context(문맥, 전후 관계): 인터피리터가 구문해석을 실행하기 위한 정보를 제공하는 역할
트리구조로 인해 복합체 패턴과 유사하게 보인다. 여기서 트리는 평가할 표현식을 포함하고, 보통 파서(Parser - Context)에 의해 생성되는데, 파서 자체는 패턴의 일부가 아니다.
// PostfixNotation.java
public class PostfixNotation {
private final String expression;
public PostfixNotation(String expression) {
this.expression = expression;
}
public void calculate() {
Stack<Integer> numbers = new Stack<>();
for (char c : expression.toCharArray()) {
switch(c) {
case '+' :
numbers.push(numbers.pop() + numbers.pop());
break;
case '-' :
int right = numbers.pop();
int left = numbers.pop();
numbers.push(left - right);
break;
default :
numbers.push(Integer.parseInt(c+""));
}
}
System.out.println(numbers.pop());
}
}
// Client.java
public class Client {
public static void main(String[] args) {
PostfixNotation notation = new PostfixNotation("123+-");
notation.calculate();
}
}
// 실행 결과
-4
// PostfixParser.java (Context)
public class PostfixParser {
public static PostfixExpression parse(String expression) {
Stack<PostfixExpression> stack = new Stack<>();
for (char c : expression.toCharArray()) {
stack.push(getExpression(c, stack));
}
return stack.pop();
}
private static PostfixExpression getExpression(char c, Stack<PostfixExpression> stack) {
switch(c) {
case '+' :
return new PlusExpression(stack.pop(), stack.pop());
case '-' :
PostfixExpression right = stack.pop();
PostfixExpression left = stack.pop();
return new MinusExpression(left, right);
default :
return new VariableExpression(c);
}
}
}
// PostfixExpression.java (AbstractExpression)
public interface PostfixExpression {
int interpret(Map<Character, Integer> context);
}
// PlusExpression.java (NonTerminalExpression)
public class PlusExpression implements PostfixExpression {
private PostfixExpression left;
private PostfixExpression right;
public PlusExpression(PostfixExpression left, PostfixExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Map<Character, Integer> context) {
return left.interpret(context) + right.interpret(context);
}
}
// MinusExpression.java (NonTerminalExpression)
public class MinusExpression implements PostfixExpression {
private PostfixExpression left;
private PostfixExpression right;
public MinusExpression(PostfixExpression left, PostfixExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Map<Character, Integer> context) {
return left.interpret(context) - right.interpret(context);
}
}
// VariableExpression.java (TerminalExpression)
public class VariableExpression implements PostfixExpression {
private Character character;
public VariableExpression(Character character) {
this.character = character;
}
@Override
public int interpret(Map<Character, Integer> context) {
return context.get(this.character);
}
}
// Client.java (Client)
public class Client {
public static void main(String[] args) {
PostfixExpression expression = PostfixParser.parse("xyz+-");
int result = expression.interpret(new HashMap<Character, Integer>() {
{
put('x', 1);
put('y', 2);
put('z', 3);
}
});
System.out.println(result);
}
}
// 실행 결과
-4
실행 결과는 동일하게 나온다. 이 패턴을 적용하면 +식과 -식이 나올 때 처리 방식을 각각 분리해서 AbstractExpression을 상속받는 클래스로 각각 구현하여 문법으로 만든다. 그리고 처리의 종착점이 되는 숫자(변수)도 AbstractExpression을 상속받는 클래스로 구현한다.
parse() 메소드를 통해 후위표현식이 중위표현식으로 파싱되고, 이후에 interpret()메소드를 통해 x, y, z에 각각 HashMap으로 값을 보내주어 해독하여 결과값을 리턴한다.
이렇게 구현할 경우 이후에 곱하기나 나누기 연산이 추가되더라도 확장이 용이하고, 재사용성이 커진다.