개발/디자인패턴

[Java][디자인 패턴] 24. 인터프리터 패턴 (Interpreter Pattern)

nova_dev 2022. 3. 28. 00:00
반응형

디자인패턴
[Java][디자인 패턴] 24. 인터프리터 패턴 (Interpreter Pattern)

인터프리터 패턴은 간단한 언어적 문법을 표현하는 패턴이다.

 인터프리터 패턴이란?

  • 프로그램을 여러 시스템 환경에서 구동하기 위해서는 추상화된 언어의 해석 과정이 필요하다.
  • 이 과정에서 언어적 해석을 담당하는 것이 바로 인터프리터(해석자) 패턴이다.

 인터프리터 패턴 구조

  • 추상 구문 트리 인터페이스 (Abstract Expression)
  • 종료 기호(Terminal Expression)
  • 비종료 기호(Non-Terminal Expression)
  • 해석기 정보(Context)
  • 문장을 나타내는 추상 구문 트리(Client)

일반적인 상황에서 해석자패턴을 직접 구현하는 일은 거의 없을거라고 생각되고 적절한 예시 코드가 떠오르지 않아 링크 코드를 참고했다.

 인터프리터 패턴 코드

1. Expression 인터페이스

interface Expression {
    List<String> interpret(Context ctx);
}

2. Context 클래스

class Context {

    private static Map<String, List<Row>> tables = new HashMap<>();

    static {
        List<Row> list = new ArrayList<>();
        list.add(new Row("John", "Doe"));
        list.add(new Row("Jan", "Kowalski"));
        list.add(new Row("Dominic", "Doom"));

        tables.put("people", list);
    }

    private String table;
    private String column;

    /**
     * Index of column to be shown in result.
     * Calculated in {@link #setColumnMapper()}
     */
    private int colIndex = -1;

    /**
     * Default setup, used for clearing the context for next queries.
     * See {@link Context#clear()}
     */
    private static final Predicate<String> matchAnyString = s -> s.length() > 0;
    private static final Function<String, Stream<? extends String>> matchAllColumns = Stream::of;
    /**
     * Varies based on setup in subclasses of {@link Expression}
     */
    private Predicate<String> whereFilter = matchAnyString;
    private Function<String, Stream<? extends String>> columnMapper = matchAllColumns;

    void setColumn(String column) {
        this.column = column;
        setColumnMapper();
    }

    void setTable(String table) {
        this.table = table;
    }

    void setFilter(Predicate<String> filter) {
        whereFilter = filter;
    }

    /**
     * Clears the context to defaults.
     * No filters, match all columns.
     */
    void clear() {
        column = "";
        columnMapper = matchAllColumns;
        whereFilter = matchAnyString;
    }

    List<String> search() {

        List<String> result = tables.entrySet()
                .stream()
                .filter(entry -> entry.getKey().equalsIgnoreCase(table))
                .flatMap(entry -> Stream.of(entry.getValue()))
                .flatMap(Collection::stream)
                .map(Row::toString)
                .flatMap(columnMapper)
                .filter(whereFilter)
                .collect(Collectors.toList());

        clear();

        return result;
    }

    /**
     * Sets column mapper based on {@link #column} attribute.
     * Note: If column is unknown, will remain to look for all columns.
     */
    private void setColumnMapper() {
        switch (column) {
            case "*":
                colIndex = -1;
                break;
            case "name":
                colIndex = 0;
                break;
            case "surname":
                colIndex = 1;
                break;
        }
        if (colIndex != -1) {
            columnMapper = s -> Stream.of(s.split(" ")[colIndex]);
        }
    }
}

3. From, Select, Where 클래스

class From implements Expression {

    private String table;
    private Where where;

    From(String table) {
        this.table = table;
    }

    From(String table, Where where) {
        this.table = table;
        this.where = where;
    }

    @Override
    public List<String> interpret(Context ctx) {
        ctx.setTable(table);
        if (where == null) {
            return ctx.search();
        }
        return where.interpret(ctx);
    }
}
class Select implements Expression {

    private String column;
    private From from;

    Select(String column, From from) {
        this.column = column;
        this.from = from;
    }

    @Override
    public List<String> interpret(Context ctx) {
        ctx.setColumn(column);
        return from.interpret(ctx);
    }
}

 

class Where implements Expression {

    private Predicate<String> filter;

    Where(Predicate<String> filter) {
        this.filter = filter;
    }

    @Override
    public List<String> interpret(Context ctx) {
        ctx.setFilter(filter);
        return ctx.search();
    }
}

6. 테스트코드

class ExpressionTest {
    @Test
    @DisplayName("인터프리터 SQL 패턴 테스트")
    void test() {
        Expression query = new Select("name", new From("people"));
        Context ctx = new Context();
        List<String> result = query.interpret(ctx);
        System.out.println(result);

        Expression query2 = new Select("*", new From("people"));
        List<String> result2 = query2.interpret(ctx);
        System.out.println(result2);

        Expression query3 = new Select("name", new From("people", new Where(name -> name.toLowerCase().startsWith("d"))));
        List<String> result3 = query3.interpret(ctx);
        System.out.println(result3);
    }
}

 원본 코드

https://www.baeldung.com/java-interpreter-pattern

참고 자료

 

 

 

 

 

반응형