본문 바로가기
Design Pattern/행동 패턴(Behavioral Patterns)

인터프리터(Interpreter) - 행동 패턴(Behavioral Patterns)

by 김 민 준 2024. 5. 25.

 

인터프리터 패턴은 주어진 언어의 문법을 나타내는 클래스를 이용해 문장을 해석하고 실해앟는 디자인 패턴이다. 주로 특정 도메인 언어 (DSL, Domain-Specific Language)의 구문 해석에 사용된다. 인터프리터 패턴은 언어의 각 기호를 클래스 하나로 표현하고, 이를 조합하여 문장을 해석하는 구조를 제공한다. 

 

주요 개념 부터 알아보자

 

1. AbstractExpression (추상 표현) : 모든 표현식의 공통 인터페이스를 정의한다.

2. TerminalExpression (종료 표현) : 문법의 기본 요소를 정의하며, 해석의 최종 단계를 나타낸다.

3. NonTerminalExpression (비종료 표현) : 문법의 조합 규칙을 정의하며, 다른 표현식을 결합한다.

4. Context (문맥) : 해석에 필요한 정보를 저장한다.

5. Client (클라이언트) : 문장을 구성하고 해석을 시작한다.

 

예시소스코드를 살펴보자.

 

수학 표현식을 해석하고 계산하는 인터프리터 패턴을 구현한다.

 

1. AbstractExpression 인터페이스

// AbstractExpression 인터페이스: 표현식의 공통 인터페이스 정의
interface Expression {
    int interpret();
}

 

2. TerminalExpression 클래스

// Number 클래스: 숫자 표현식 (종료 표현)
class Number implements Expression {
    private int number;

    public Number(int number) {
        this.number = number;
    }

    @Override
    public int interpret() {
        return number;
    }
}

 

3. NonTerminalExpression 클래스

 

// Addition 클래스: 덧셈 표현식 (비종료 표현)
class Addition implements Expression {
    private Expression leftExpression;
    private Expression rightExpression;

    public Addition(Expression leftExpression, Expression rightExpression) {
        this.leftExpression = leftExpression;
        this.rightExpression = rightExpression;
    }

    @Override
    public int interpret() {
        return leftExpression.interpret() + rightExpression.interpret();
    }
}

// Subtraction 클래스: 뺄셈 표현식 (비종료 표현)
class Subtraction implements Expression {
    private Expression leftExpression;
    private Expression rightExpression;

    public Subtraction(Expression leftExpression, Expression rightExpression) {
        this.leftExpression = leftExpression;
        this.rightExpression = rightExpression;
    }

    @Override
    public int interpret() {
        return leftExpression.interpret() - rightExpression.interpret();
    }
}

 

 

4. Client 클래스

 

// Client 클래스: 수학 표현식을 구성하고 해석을 시작
public class InterpreterPatternDemo {
    public static void main(String[] args) {
        // 표현식: (5 + 3) - (2 + 1)
        Expression expr1 = new Addition(new Number(5), new Number(3));
        Expression expr2 = new Addition(new Number(2), new Number(1));
        Expression mainExpr = new Subtraction(expr1, expr2);

        // 해석 및 결과 출력
        int result = mainExpr.interpret();
        System.out.println("Result: " + result);  // 출력: Result: 5
    }
}

 

 

AbstractExpression 인터페이스: interpret 메서드를 정의하여 모든 표현식의 공통 인터페이스를 제공한다.

 

TerminalExpression 클래스: 기본적인 숫자와 같은 종료 표현식을 정의한다.

 

NonTerminalExpression 클래스: 덧셈, 뺄셈 등의 비종료 표현식을 정의하며, 다른 표현식을 결합하여 더 복잡한 표현식을 만든다.


Client 클래스: 표현식을 구성하고 해석을 시작한다.

 

 

현업에서 주로 사용하는 부분은 이커머스의 할인쪽 로직을 구현하기도 하다.

 

// Expression 인터페이스: 모든 표현식의 공통 인터페이스 정의
interface Expression {
    boolean interpret(Order order);
}

 

// TerminalExpression 클래스: 간단한 조건 표현식 정의
class MinimumAmountExpression implements Expression {
    private double amount;

    public MinimumAmountExpression(double amount) {
        this.amount = amount;
    }

    @Override
    public boolean interpret(Order order) {
        return order.getTotalAmount() >= amount;
    }
}

class CustomerTypeExpression implements Expression {
    private String customerType;

    public CustomerTypeExpression(String customerType) {
        this.customerType = customerType;
    }

    @Override
    public boolean interpret(Order order) {
        return order.getCustomerType().equalsIgnoreCase(customerType);
    }
}

 

// AndExpression 클래스: 두 표현식을 AND 연산으로 결합
class AndExpression implements Expression {
    private Expression expr1;
    private Expression expr2;

    public AndExpression(Expression expr1, Expression expr2) {
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    @Override
    public boolean interpret(Order order) {
        return expr1.interpret(order) && expr2.interpret(order);
    }
}

 

// Order 클래스: 해석 대상이 되는 주문 정보
class Order {
    private double totalAmount;
    private String customerType;

    public Order(double totalAmount, String customerType) {
        this.totalAmount = totalAmount;
        this.customerType = customerType;
    }

    public double getTotalAmount() {
        return totalAmount;
    }

    public String getCustomerType() {
        return customerType;
    }
}

 

// Client 클래스: 비즈니스 규칙을 구성하고 해석
public class InterpreterPatternBusinessRuleDemo {
    public static void main(String[] args) {
        // 주문 생성
        Order order = new Order(150.0, "Regular");

        // 할인 규칙 정의
        Expression rule = new AndExpression(
            new MinimumAmountExpression(100.0),
            new CustomerTypeExpression("Regular")
        );

        // 규칙 해석 및 적용
        boolean isEligibleForDiscount = rule.interpret(order);
        System.out.println("Is order eligible for discount? " + isEligibleForDiscount);
    }
}

 

//출력

//Is order eligible for discount? true

 

현업에서는 사실 많이 쓰이지는 않는다. 대체적으로 복잡한 경우가 많고, 성능에서도 여러 규칙을 해석할때 문제가 될 수 있기 때문이다.