Behavioural Design Patterns: Interpreter

Previously we had a look at the chain of responsibility and the command pattern. They do tackle different problems however they both delegate an action to another component, the command patterns always delegates the action, the chain of responsibility if the action cannot be handled it will be forward it to its successor.

We are going to switch context and check the interpreter pattern.
The interpreter pattern specifies how to evaluate custom languages.
The examples are limitless, for example the spel language for spring or even a sql like language.

We will keep things simple and create an interpreter for a simple query language only with select and where statements. This is not impossible in the real world, just thing about dynamodb and how simple its expressions are, the examples are countless.

So let’s start with our clause interface.

package com.gkatzioura.design.behavioural.interpreter;

public interface Clause{

}

I will implement the WhereClause

package com.gkatzioura.design.behavioural.interpreter;


public class WhereClause implements Clause {

    /**
     * Add values related to the where clause functions and expressions
     */

    public WhereClause() {
    }

    /**
     * Add functions for expressions for equals not equals etc.
     */

}

And the SelectClause

package com.gkatzioura.design.behavioural.interpreter;

import java.util.Optional;

public class SelectClause implements Clause {

    private Optional<WhereClause> optWhereClause = Optional.empty();

    public <T> T load(Class<T> valueType) {

        /**
         * Apply filter according to the where clause
         */
        if(optWhereClause.isPresent()) {

        }

        /**
         * Load data
         */

        /**
         * Serialize/Deserialize data based on the type
         * For example if you use jackson it will be:  new ObjectMapper().readValue("text",valueType);
         */


        return null;
    }

    public void setWhereClause(WhereClause whereClause) {
        this.optWhereClause = Optional.of(whereClause);
    }
}

The load function shall load our data based on the expression given.

Both clauses are linked together however the select clause can produce results even when ‘where’ statements have not been given whatsoever. In any case when it comes to loading the data the select clause will check and apply any filter statements that the where clause might have (if exists) and then return the data.
Let’s move to the interpreter specific parts

We will create the expression interface.

package com.gkatzioura.design.behavioural.interpreter;

public interface Expression {

    Clause interpret(String context);

}

The interpretation of the text given will give us back a clause.

We shall create the WhereExpression.

package com.gkatzioura.design.behavioural.interpreter;

public class WhereExpression implements Expression {

    @Override
    public WhereClause interpret(String context) {
        WhereClause whereClause = new WhereClause();
        /**
         * Parse the string and find any where statements ie. A=1 AND B!=2 and apply them.
         */

        return whereClause;
    }

}

And the Select Expression.

package com.gkatzioura.design.behavioural.interpreter;

public class SelectExpression implements Expression {

    @Override
    public SelectClause interpret(String context) {
        SelectClause selectClause = new SelectClause();

        /**
         * Parse text and check for expressions like `SELECT * FROM ` or `SELECT A,B,C FROM` .
         */

        return selectClause;
    }

}

The last step would be the query expression.

package com.gkatzioura.design.behavioural.interpreter;

public class QueryExpression implements Expression {

    @Override
    public SelectClause interpret(String context) {

        SelectClause selectClause = new SelectExpression().interpret(context);

        if(context.contains("WHERE")) {

            WhereClause whereClause = new WhereExpression().interpret(context);
            selectClause.setWhereClause(whereClause);
        }

        return selectClause;
    }

}

So let’s put them all together. We shall interpret a query which fetches a list of integers.

package com.gkatzioura.design.behavioural.interpreter;

public class Interpreter {

    public static void main(String[] args) {
        String selectionString = "SELECT b FROM foo WHERE a=1";
        Integer[] values = new QueryExpression()
                .interpret(selectionString)
                .load(Integer[].class);
    }

}

You can find the sourcecode on github.