September 18, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Working With Design Patterns: Interpreter

  • May 22, 2008
  • By Jeff Langr
  • Send Email »
  • More Articles »

The Design Patterns book suggest that the interpreter pattern is the same as the composite design pattern, except for intent. A composite is an interpreter when the resulting hierarchical structure represents something you might consider a grammar. Technically, then, the example I built for composite is an implementation that also represents an interpreter! So I need go no further here, right?

No, I'm not done here, and for the primary reason that there are few good examples available to help developers understand interpreters. My example here perhaps isn't perfect, but I'm hoping it adds a bit more understanding than all the other examples I found when I searched for "interpreter design pattern."

My goal is to build an interpreter that supports a simple boolean language. The language in turn is the basis for a set of simple commands to move documents from one folder to another, based on dynamic criteria. Think "resume filter" as a potential application. For example, one command might be:

   move from incomingResumes to phoneScreen when
      contains fortran or smalltalk and olderThan 01/01/2006

Here, I'll concern myself with the second part of the expression, starting with "contains," because the interpreter pattern is most appropriate when the expression is something that can resolve to a single result.

Because I'm working with documents, I'll need a simple document class to demonstrate use of the interpreter. Listing 1 shows a TextDocument implementation that supports a contains method and also encapsulates a creation date. Because I'm always working in a TDD fashion, Listing 2 shows tests for the TextDocument class.

Listing 1: Documents.

// Document.java:
public interface Document {
   public boolean contains(String... keywords);

   public java.util.Date getDate();
}

// TextDocument.java
import java.util.*;

public class TextDocument implements Document {
   private final String contents;
   private final Date date;

   public TextDocument(Date date, String contents) {
      this.date = date;
      this.contents = contents;
   }

   @Override
   public boolean contains(String... textElements) {
      for (String text: textElements)
         if (contents.contains(text))
            return true;
      return false;
   }

   @Override
   public Date getDate() {
      return date;
   }
}

Listing 2: A test for Contains.

import static org.junit.Assert.*;

import java.util.*;
import org.junit.*;

public class ContainsTest {
   private static final String CONTENTS = "these are the contents";
   private TextDocument document;

   @Before
   public void createDocument() {
      document = new TextDocument(new Date(), CONTENTS);
   }

   @Test
   public void failsWhenTextNotInContents() {
      Expression expression = new Contains(CONTENTS + "x");
      assertFalse(expression.evaluate(document));
   }

   @Test
   public void passesWhenTextInContents() {
      String text = "contents";
      assertTrue(CONTENTS.indexOf(text) != -1);

      Expression expression = new Contains(text);
      assertTrue(expression.evaluate(document));
   }
}

The basic direction for building an interpreter, using this design pattern, is to translate the grammar at hand into a set of classes. A class represents each rule in the grammar. Fields on that class represent the symbols to which a rule translates. For example, in my little sub-grammar, there is a rule that looks like:

   And ::= Expression 'and' Expression

Thus, my implementation of the interpreter pattern includes a class named And, with two fields, each containing an Expression object. (Instead of creating a separate object to represent text, such as "and," I usually can embed it directly into code in the rule class, And in this case.)

With basic support in place, I can start creating simple rule classes that can work on Document objects. For example, Contains (see Listing 3) indicates whether or not a document contains any of the keywords passed in. OlderThan (see Listing 4) determines whether or not a document is older than a specified date. Each of these inquiries implements the Expression interface (see Listing 5), allowing clients to evaluate the condition.





Page 1 of 6



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel