Chitika

Friday, October 22, 2004

implementing Visitor pattern

During a development of any project, there will be many times where we have to deal with Collection classes, especially List & Map. Sometimes we have to iterate through a List to find an object which matches our criteria. Sometimes we iterate to filter out elements of the List which does not meet our purpose. Sometimes we iterate the List to summarize their values. There are plenty of stuffs we can perform while we're iterating a List.

Currently, I'm trying to implement the Visitor pattern while iterating through a List. There are several cases which I encounter, which requires me to iterate through a List and filter out elements of that List which does not meet the criteria set earlier. At first, this may seem to be a simple thing to do, just iterate, compare & remove. But, living up to the DRY (Don't Repeat Yourself) paradigm, I'm trying to *think* a level of abstraction to the problem.

Here's what a simple filter code would look like:

for (ListIterator it = aList.listIterator ();
     it.hasNext ();) {
  Customer c = (Customer) it.next ();
  String occupation = c.getOccupation ();
  if (occupation == null ||
      occupation.equals ("java developer")) {
    it.remove ();
  }
}

Now, when I require the similar filter logic (iterate, compare & remove) to be reused for different sets of data and compare rules, I'd have to recode the whole iteration again. And, this type of thing tends to increase in numbers before a project ends.

So, here's my current approach. I'm declaring an interface in which every filter class needs to implement.

public interface ListFilter {
  public boolean passes (Object o);
}

This filter will be used within the generic iteration. Here's an example where I use the generic (iterate, compare & remove) logic to filter the List based on more than one filter rules.

public static List filterList (
    List original, ListFilter[] filters) {
  if (original == null ||
      filters == null || filters.length <= 0) {
    return null;
  }

  // assume you have a method to clone the List
  List cloned = cloneList (original);

  for (ListIterator it = cloned.listIterator ();
       it.hasNext ();) {
    Object o = it.next ();
    for (int i=0; i<filters.length; i++) {
      ListFilter filter = filters[i];
      if (filter != null && !filter.passes(o)) {
        it.remove();
        break;
      }
    }
  }

  return cloned;
}

For the above sample case, where we'd like to filter out all unemployed Customers and all java developers from the sales options the company is trying to promote, then we could have implemented the ListFilter as follows:

public class OccupationFilter implements ListFilter {

  private List forbiddenOccupations;

  public OccupationFilter (List forbiddenOccupations) {
    this.forbiddenOccupations = forbiddenOccupations;
  }

  public boolean passes (Object o) {
    if (o != null && o instance of Customer) {
      if (forbiddenOccupations != null &&
          forbiddenOccupations.size() > 0) {
        Customer c = (Customer) o;
        String occupation = c.getOccupation ();
        if (forbiddenOccupations.indexOf(occupation) >= 0)
        {
          return false;
        } else {
          return true;
        }
      } else {
       return true;
      }
    }
    return false;
  }
}

And, since our generic filter logic is capable of applying multiple filters during a single iteration, it helps us to easily add more filters as we see fit. These filters can even act as singleton if they don't have a dynamic part of their rules.

I'm currently trying to abstract out a summary logic (iterate, compare, summary if necessary) from the same List iteration. I hope I can find a neat way to do it.. :D

Further Reading:
Refactoring: Improving the Design of Existing Code
Design Patterns
Head First Design Patterns


No comments:

Post a Comment