인터프리터 패턴
자주 등장하는 문제를 간단한 언어로 정의하고 재사용하는 패턴
- 반복되는 문제 패턴을 언어 또는 문법으로 정의하고 확장할 수 있다. ex. 정규표현식
[참고]
인터프리터는 사람이 작성한 코드를 하드웨어가 이해할 수 있도록 변환해주는 장치이다.
다이어그램을 보면 컴포짓 패턴과 매우 유사하다.
- Context 는 모든 Expression 에서 사용하는 공통된 정보가 담겨있다.
- Expression 은 우리가 표현하는 문법을 나타내는데 Context 가 들어있는 것을 볼 수 있다.
- TerminalExpression 은 그 자체로 종료되는 Expression 이고,
- Non TerminalExpression 은 다른 Expression 들을 재귀적으로 참조하고 있는 Expression 이다.
인터프리터 패턴 적용
후위연산을 하는 코드가 있다고 하자.
public class PostfixNotation {
private final String expression;
public PostfixNotation(String expression) {
this.expression = expression;
}
public static void main(String[] args) {
PostfixNotation postfixNotation = new PostfixNotation("123+-");
postfixNotation.calcultate();
}
private void calcultate() {
Stack<Integer> st = new Stack<>();
for(char c : this.expression.toCharArray()) {
switch (c) {
case '+':
st.push(st.pop() + st.pop());
break;
case '-':
int right = st.pop();
int left = st.pop();
st.push(left - right);
break;
default:
st.push(Integer.parseInt(c+""));
}
}
System.out.println(st.pop());
}
}
"123+-" 같은 연산을 문법으로 정의하여 재사용 할 것이라면 "123+-" 를 트리구조로 표현할 수 있다.
"123" 은 그대로 "123" 반환하면 되는 TerminalExpression 이다.
하지만 "+-" 와 같은 Expression 은 다른 두개의 Expression 을 Interpreter 한 다음 그 결과를 연산하는 Non TerminalExpression 이다.
App
public class App {
public static void main(String[] args) {
PostfixExpression expression = PostfixParse.parse("xyz+-a+");
int result = expression.interpreter(Map.of('x',1,'y',2,'z',3, 'a',4));
System.out.println(result);
}
}
PostfixParse.parse 메소드로 문자열을 파싱한다.
PostfixParser
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);
}
}
}
parse 메소드를 보면 받은 문자열을 Character 타입으로 나눈 뒤 getExpression 메소드를 사용하여 stack 에 넣어준다.
그런데 parse 의 반환 타입과 스택의 타입을 보면 모두 PostfixExpression 인 것을 확인 할 수 있다.
PostfixParse
public interface PostfixExpression {
int interpreter(Map<Character, Integer> context);
}
모든 Expression 들은 위 인터페이스를 구현하면 된다.
먼저 값을 그대로 반환하는 Terminal Expression 인 VariableExpression 을보자.
public class VariableExpression implements PostfixExpression {
private Character character;
public VariableExpression(Character character) {
this.character = character;
}
@Override
public int interpreter(Map<Character, Integer> context) {
return 0;
}
}
Non Terminal Expression 인 PlusExpression 과 MinusExpression 은 아래와 같다.
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 interpreter(Map<Character, Integer> context) {
return left.interpreter(context) + right.interpreter(context);
}
}
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 interpreter(Map<Character, Integer> context) {
return left.interpreter(context) - right.interpreter(context);
}
}
이를 디버거를 통해 보면 아래와 같고, 이는 트리 형태를 가지고 있다.
장점과 단점
장점
- 자주 등장하는 문제 패턴을 언어와 문법으로 정의할 수 있다.
- 기존 코드를 변경하지 않고 새로운 Expression을 만들 수 있다.
단점
- 복잡한 문법을 표햔하려면 Expression와 Parser가 복잡해진다.
실무에선 어떻게 쓰이나?
자바
- 정규표현식
스프링
- expression language
참고
'디자인 패턴' 카테고리의 다른 글
중재자 패턴 (Mediator pattern) (0) | 2022.08.11 |
---|---|
이터레이터 패턴 (Iterator Pattern) (0) | 2022.08.04 |
커맨드 패턴 (Command Pattern) (0) | 2022.08.02 |
책임 연쇄 패턴 (Chain of Responsibility Pattern) (0) | 2022.08.02 |
프록시 패턴 (Proxy Pattern) (0) | 2022.07.22 |