usable in any place a human can be used

20091009

sharpening the saw

The other day I was reading some delicious GoF and even though it was published 15 years ago, it still is a great read. I've never read it before and going into it I read several wise people basically saying the same thing, "After reading it, don't go overboard."

Apparently, there is a propensity to overuse the patterns and make a bigger mess than using no patterns at all. Being fully aware of this fact, and with the good fortune of not being a silly n00b and actually having experience using many of the design patterns I'm currently reading about in the real world, it got me thinking as to why people would overuse the patterns. I think it is because the book is a sublime piece of work, the patterns are expressed succinctly, the examples are clear, and the code that is produced is readable and understandable. The problem domains are complex but well suited to pattern application. In reading the book you get an impression that patterns make things better, and that because when skillfully applied they generally do.

The problem is that there is a lack of examples of when a pattern bites you, when the inartful application of patterns leaves you fighting against an army of delegates, bridges, and command objects. This got me thinking about pattern-abuse's cousin, the anti-pattern.

The anti-pattern is something that very few people ever study or know the names of, but they pop up all over and most of us have to deal with them on a daily basis. For education's sake I've picked the best one's from Wikipedia's excellent list.

Abstraction Inversion -

An anti-pattern arising when users of a construct need functions implemented within it but not exposed by its interface. The result is that the users re-implement the required functions in terms of the interface, which in its turn uses the internal implementation of the same functions.
The most egregious example of this that I can think of is languages that don't treat functions as first class objects. This is the cause of fun code like this

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Locale;
import static java.lang.System.out;
import static java.lang.System.err;

public class Deet<T> {
  private boolean testDeet(Locale l) {
    // getISO3Language() may throw a MissingResourceException
    out.format("Locale = %s, ISO Language Code = %s%n", l.getDisplayName(), l.getISO3Language());
    return true;
  }

  private int testFoo(Locale l) { return 0; }
  private boolean testBar() { return true; }

  public static void main(String... args) {
    if (args.length != 4) {
      return;
    }
    
    try {
      Class<?> c = Class.forName(args[0]);
      Object t = c.newInstance();

      Method[] allMethods = c.getDeclaredMethods();
      for (Method m : allMethods) {
        String mname = m.getName();
        if (!mname.startsWith("test") || (m.getGenericReturnType() != boolean.class)) {
          continue;
        }
        Type[] pType = m.getGenericParameterTypes();
        if ((pType.length != 1) || Locale.class.isAssignableFrom(pType[0].getClass())) {
          continue;
        }

        out.format("invoking %s()%n", mname);
        try {
          m.setAccessible(true);
          Object o = m.invoke(t, new Locale(args[1], args[2], args[3]));
          out.format("%s() returned %b%n", mname, (Boolean) o);

          // Handle any exceptions thrown by method to be invoked.
        } catch (InvocationTargetException x) {
          Throwable cause = x.getCause();
          err.format("invocation of %s failed: %s%n", mname, cause.getMessage());
        }
     }

     // production code should handle these exceptions more gracefully
  } catch (ClassNotFoundException x) {
    x.printStackTrace();
  } catch (InstantiationException x) {
    x.printStackTrace();
  } catch (IllegalAccessException x) {
    x.printStackTrace();
  }
}

Expection Handling (a portmanteau of expect and exception) -

Using a language's error handling system to implement normal program logic
You shouldn't do this, if you are, you are doing your job wrong, stop that. The catch block should be used to perform exception handling, that's it.

Golden Hammer aka Silver Bullet -

"When the only tool you have is a hammer, it is tempting to treat everything as if it were a nail. - Abraham Maslow
If you read the GoF Design Patterns book and then proceed to use Design Patterns everywhere, this is your anti-pattern!

The GoF book was so groundbreaking because it allowed us to encapsulate and share knowledge. Design Patterns are to our brains and conversations what classes are to compilers. For the first time we could talk to people we didn't know about implementing the Visitor pattern to solve a problem, and all parties involved knew what the other was talking about.

Maybe its time someone wrote a comprehensive anti-pattern book, and so we could stop saying that code smells, and really identify the anti-pattern at work. Being able to identify bad code is just as important as being able to write good code.

I only scratched the surface of anti-patterns. Go read the rest of the list for yourself, then try to identify if you are using anti-patterns in your code, then REFACTOR!

No comments:

Post a Comment