August 28, 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 »

Listing 13: Supporting an additional terminal expression.

import java.text.*;
import java.util.*;

public class Parser {
   private List<String> arguments = new ArrayList<String>();

   public Expression parse(String expression) {
      String command = null;
      String[] tokens = expression.split(" ");
      for (String token: tokens)
         if (isKeyword(token))
            command = token;
         else
            pushArgument(token);
      return createExpression(command);
   }

   private Expression createExpression(String command) {
      if (command.equals("contains"))
         return new Contains
            ((String[])arguments.toArray(new String[0]));
      if (command.equals("olderThan"))
         return new OlderThan(parseDate(arguments.get(0)));
      return null;
   }

   private Date parseDate(String textDate) {
      try {
         return new
            SimpleDateFormat("MM/dd/yyyy").parse(textDate);
      } catch (ParseException unexpected) {
         return null;
      }
   }

   private boolean isKeyword(String token) {
      return token.equals("contains") ||
         token.equals("olderThan");
   }

   private void pushArgument(String token) {
      arguments.add(token);
   }
}

Well, my Parser so far is a bit messy. It's now very obvious that there is a violation of responsibilities in the Parser class because it must understand how to parse differing arguments for the associated Expression objects. I'm also not fond of the notion of having to hold onto a string representing the command, and then instantiating it later when arguments can be attached. Maybe following the Java bean convention is more appropriate: I'll change the Expression classes to support a no-argument constructor as well as a setter for arguments.

After about a dozen minutes of incremental refactoring, I end up with a cleaner solution, shown in Listing 14.

Listing 14: A refactored Parser.

import java.util.*;

public class Parser {
   private List<String> arguments = new ArrayList<String>();

   public Expression parse(String expressionText) {
      String command = null;
      String[] tokens = expressionText.split(" ");
      for (String token: tokens)
         if (isKeyword(token))
            command = token;
         else
            arguments.add(token);

      Expression expression = createExpression(command);
      expression.setArgs(arguments);
      return expression;

   }

   private Expression createExpression(String command) {
      if (command.equals("contains"))
         return new Contains();
      if (command.equals("olderThan"))
         return new OlderThan();
      return null;
   }

   private boolean isKeyword(String token) {
      return token.equals("contains") ||
         token.equals("olderThan");
   }
}

Each of the Expression implementations changes a bit (see Listing 15), to support a no-arg constructor and to supply a setArgs method.

Listing 15: A reworked Expression sub-type interface.

import java.util.*;

public class Contains implements Expression {
   private List<String> keywords = new ArrayList<String>();

   @Override
   public boolean evaluate(Document document) {
      return document.contains
         ((String[])keywords.toArray(new String[0]));
   }

   String[] getKeywords() {
      return (String[])keywords.toArray(new String[0]);
   }

   @Override
   public void setArgs(List<String> args) {
      this.keywords = args;
   }
}




Page 4 of 6



Comment and Contribute

 


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

 

 


Sitemap | Contact Us

Rocket Fuel