Monday, August 16, 2010

Don't Reuse. Rewrite!

As Isaac Newton said, "If I have seen further it is only by standing on the shoulders of giants." This is the promise of reuse. By taking advantage of libraries and frameworks we are able to write large complex systems at a fraction of the time and effort that it would be without.

And yet...

Just like anything else in an engineering discipline, it is about tradeoffs. Using an external library causes an external dependency. An external dependency is one more thing that has to be learned to understand a whole system. It may have bugs or just be poorly documented. Two years from now when you are moving to new hardware, it may not support the new hardware. In short using a third party library is far from free even if it is free as in beer or speech.

"If it's a core business function -- do it yourself, no matter what."So when should you use an existing library vs. writing it yourself? Joel Spolsky says, "If it's a core business function -- do it yourself, no matter what." This is is good advice. However, I would also say that if the effort of writing it yourself is not much more than that of integrating the library, then you should write it yourself.

So what brought this up? On my last post about JNDI, SQL, and JUnit, I used a few third party packages. Joseph Graves emailed me and told me how he had solved the SQL problem by writing his own mock JDBC library, and that it looked like it would be simple to do so for JNDI. While I still think using HSQLDB was the right thing for my situation, he had a good point about JNDI. Did I really need to add an external dependency when all I wanted was effectively a global Map? And so I rewrote the JNDI part as follows:
  1 import java.util.HashMap;
2 import java.util.Hashtable;
3 import java.util.Map;
5 import javax.naming.Binding;
6 import javax.naming.Context;
7 import javax.naming.Name;
8 import javax.naming.NameClassPair;
9 import javax.naming.NameParser;
10 import javax.naming.NamingEnumeration;
11 import javax.naming.NamingException;
12 import javax.naming.spi.InitialContextFactory;
14 public class MockInitialContextFactory implements InitialContextFactory {
16 private static Context sInitialContext;
18 public synchronized Context getInitialContext(Hashtable<?, ?> environment)
19 throws NamingException {
20 if (sInitialContext == null) {
21 sInitialContext = new MockContext();
22 }
23 return sInitialContext;
24 }
27 static class MockContext implements Context {
29 private Map<String, Object> mMap = new HashMap<String, Object>();
31 public Object lookup(String name) throws NamingException {
32 return mMap.get(name);
33 }
35 public void bind(String name, Object obj) throws NamingException {
36 mMap.put(name, obj);
37 }
39 // remainder of auto-generated stub methods with default implementation
40 }
41 }
As you can see, with just a handful of meaningful lines of code (and Eclipse's stub generation) I was able to remove the dependency on two third party jars, thereby simplifying the whole system.

1 comment:

Grant Gardner said...

Thanks, it really is that simple isn't it!.