Computer Science Canada

Teaching Idiomatic Code?

Author:  wtd [ Wed Nov 30, 2005 2:45 am ]
Post subject:  Teaching Idiomatic Code?

Reinventing the Wheel Doesn't Rule out Generalized Code

In education it's quite natural to reinvent the wheel. Doing so can provide lessons that simply using the API cannot.

For instance, let's say we want to take an ArrayList of Name objects and find all of the Names where the first name is "Bob". We'll assume the Name class has getFirstName and getLastName methods.

Java:
ArrayList<Name> acceptableNames = new ArrayList<Name>();

for (Name currentName : names) {
   if (currentName.getFirstName().equals("Bob")) {
      acceptableNames.add(currentName);
   }
}


That's reasonably straightforward. Now let's add code to get all of the names where the last name is "Smith".

Java:
ArrayList<Name> acceptableNames = new ArrayList<Name>();

for (Name currentName : names) {
   if (currentName.getFirstName().equals("Bob")) {
      acceptableNames.add(currentName);
   }
}

acceptableNames = new ArrayList<Name>();

for(Name currentName : names) {
   if (currentName.getLastName().equals("Smith")) {
      acceptableNames.add(currentName);
   }
}


So, why is this wrong?

Well, when we wrote the first search for all Names that meet a certain criteria, we reinvented the wheel and we discovered how to do this kind of thing. That was enough. The subsequent code is just redundant.

The next lesson should be how to generalize the code we've written; how to make it into a tool we can use widely in our code.

How can we separate the logic? Well, we need to have one piece of code that handles the looping and creation of the new ArrayList, and be able to pass in any kind of condition we want.

For the condition, we'd use an interface with which we can create anonymous inner classes.

Java:
interface MatchCriteria<T> {
   public boolean matches(T a);
}


And then we'd create a utility class with a static method which can do the search.

Java:
class Find {
   static <T> ArrayList<T> findAll(Iterable<T> items, MatchCriteria<T> c) {
      ArrayList<T> output = new ArrayList<T>();
     
      for (T currentItem : items) {
         if (c.matches(currentItem)) {
            output.add(currentItem);
         }
      }
     
      return output;
   }
}


Now we can select all of the Names with a First name of "Bob" like so:

Java:
ArrayList<Name> acceptableNames = Find.findAll(names, new MatchCriteria<Name>() {
   public boolean matches(Name a) {
      return a.getFirstName().equals("Bob");
   }
});


Or to find all names that have a last name of "Smith".

Java:
ArrayList<Name> acceptableNames = Find.findAll(names, new MatchCriteria<Name>() {
   public boolean matches(Name a) {
      return a.getLastName().equals("Smith");
   }
});


We can take this further and create a "my.utils" package or such and place MatchCriteria and Find into it for future reuse. Of course, then we'd have to deal with the classpath.

Rather than mindlessly copying and pasting code, this approach would encourage both thought of how to implement algorithms, and instilling an appreciation for code re-use, which is critical to an understanding of the Java way of getting work done.

So, why does this seem so rare?

I certainly can't speak or everyone, but what experience I do have suggests that the dominant reason is that teachers are afraid of overwhelming students. This seems to me a poor excuse. Teaching idiomatic practices is absolutely essential, and trying to work around that indicates that a language with fewer or different idioms should probably have been used in the first place.


: