diff --git "a/\355\226\211\353\217\231/8\354\243\274\354\260\250-\354\235\270\355\204\260\355\224\204\353\246\254\355\204\260/example-code.md" "b/\355\226\211\353\217\231/8\354\243\274\354\260\250-\354\235\270\355\204\260\355\224\204\353\246\254\355\204\260/example-code.md" new file mode 100644 index 0000000..dd2b818 --- /dev/null +++ "b/\355\226\211\353\217\231/8\354\243\274\354\260\250-\354\235\270\355\204\260\355\224\204\353\246\254\355\204\260/example-code.md" @@ -0,0 +1,148 @@ +## 2. 인터프리터 적용 예제 + +### 2-1. 요구사항 + +자주 등장하는 문제 ➡️ 간단한 언어 로 정의하고 재사용하는 패턴으로 + +반복되는 문제 패턴을 언어 또는 문법으로 정의하고 확장할 수 있기 때문에 후위표현식의 연산을 문법으로 정의하는 코드를 작성하고자 하고 싶다. + +아래 코드를 살펴보자. + +```java +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.calculate(); + } + + private void calculate() { + Stack numbers = new Stack<>(); + + for (char c : this.expression.toCharArray()) { + switch (c) { + // Java 14부터 switch-case break 없이 사용할 수 있다! + case '+' -> numbers.push(numbers.pop() + numbers.pop()); + case '-' -> { + int right = numbers.pop(); + int left = numbers.pop(); + numbers.push(left - right); + } + default -> numbers.push(Integer.parseInt(c + "")); + } + } + + System.out.println(numbers.pop()); + } +} +``` + +`123+-`연산을 `xyz+-`문법으로 정의하여 재사용하는 경우, `xyz`는 TerminalExpression이고 `+-`는 NonTerminalExpression이다. + +- `TerminalExpression` : 표현에서 사용되는 기호들(x,y,z)의 해석값을 구하는 클래스(즉 x일때 무엇, y일때 무엇, z일때 무엇으로 해석하나) +- `NonTerminalExpression` : 기호만으로는 결과가 나오지 않고 특정 `Expression`들을 참조해서 결과를 얻어야하는 클래스(+, -만으로는 결과가 나오지 않고 z, x, y과 같은 `TerminalExpression`, 또는 또 다른 `NontermialExpresion` 참조해서 결과값을 얻을 때까지 재귀적으로 참조한다. ) + +이제 클라이언트 코드부터, 인터프리터 패턴으로 개선한 코드를 살펴보자. + +```java +// 클라이언트 +public class App { + public static void main(String[] args) { + // PostfixParser - 문자열을 parse 해준다. + PostfixExpression expression = PostfixParser.parse("xyz+-a+"); + int result = expression.interpret(Map.of('x', 1, 'y', 2, 'z', 3, 'a', 4)); + } +} + +// 받은 문자열을 Expression들을 이용해 파싱하는 리턴 값은 Expression 객체들로 이루어진 Expression을 반환 +public class PostfixParser { + // 문자열을 char 배열로 받아서 getExpression 메소드 호출 후 stack push + public static PostfixExpression parse(String expression) { + // Stack 타입 - PostfixExpression + Stack stack = new Stack<>(); + for (char c : expression.toCharArray()) { + stack.push(getExpression(c, stack)); + } + return stack.pop(); + } + + private static PostfixExpression getExpression(char c, Stack 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); + } + } +} + +// AbstractExpression +public interface PostfixExpression { + // 모든 표현에서 공통적으로 구현할 메소드 + int interpret(Map context); +} + +// TerminalExpression +// 특정 기호들에 대한 해석값을 정의 +public class VariableExpression implements PostfixExpression { + + private Character character; + + public VariableExpression(Character character) { + this.character = character; + } + + @Override + public int interpret(Map context) { + // 값을 그대로 반환 + return context.get(this.character); + } +} + +// NonTerminalExpression1 (Plus) +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 context) { + // 받은 기호만으로 결과가 나오지 않아 다른 Expression들을 참조하여 결과 도출 + return left.interpret(context) + right.interpret(context); + } +} + +// NonTerminalExpression2 (Minus) +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 context) { + // Plus와 마찬가지 + return left.interpret(context) - right.interpret(context); + } +} +``` + +디버깅을 해보면 아래와 같고, 트리 형태이다. + +image + +image