-
인터프리터 패턴 (Interpreter Pattern)디자인 패턴 2022. 8. 3. 22:31
인터프리터 패턴
자주 등장하는 문제를 간단한 언어로 정의하고 재사용하는 패턴
- 반복되는 문제 패턴을 언어 또는 문법으로 정의하고 확장할 수 있다. 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