ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Better, Faster, Lighter Programming in .NET and Java
Pages: 1, 2, 3

Principle 4. You Are What You Eat

Anyone who has heard me talk about enterprise development has heard me say, at least once, that the tools you use aren't just tools, they're risks. Every time you use somebody's framework, or IDE, or library, or interface, or what-have-you, you are essentially investing in that team/person/company. If that tool suddenly goes away, or takes a sharp right turn when you were hoping for a left, then your project might suffer as a result. Additionally, when you ship applications to your customers, if something goes wrong (even deep down in some third-party library you got from the Internet), your customer will come back after you, not the third party. Your application is always your responsibility; if you make use of a buggy framework, those bugs will reflect back on you.

In the Java world, there are a hundred ways to do anything. If you want an MVC framework, there's Struts, WebWork, SpringMVC, and one implementation for every J2EE vendor. For O/R mapping, there's Hibernate, JDO, Kodo, EJBs, etc. Heck, there are even five or six viable IDEs. On the Microsoft side of the world, we have tended to be less diverse. We use Visual Studio for development, ADO.NET for data access, and ASP.NET for our web front end. There aren't nineteen different web form strategies (Tapestry, Velocity, JavaServer Faces, etc.). However, that focus on a single vendor is starting to change.

If you take a look at SourceForge, you will notice that there are almost as many .NET projects as Java projects now. All of the major tools on the Java side are being ported to .NET. This list includes:

  • Struts.NET for web MVC
  • NHibernate for O/R mapping
  • NUnit for unit testing
  • Pico.NET and Spring.NET for lightweight alternatives to EnterpriseServices/COM+
  • NAnt for build management
  • CruiseControl.NET and Draco.NET for integration builds
  • C# support in Eclipse and IDEA

And the list goes on. As the world of alternatives begins to open, Microsoft developers will now have to face the decisions that Java programmers face all the time: which implementation is right for this team, and this project? Before, you just used what was available. Now, there are competing strategies at every turn.

Making the right decision about your tools always comes down to a cost/benefit analysis. The cost side of the equation is some amalgam of money, research time, training, performance, and sustainability ("Will this product be around for as long as my project?"). The benefits can take the form of simplified development and maintenance, better performance or scalability, or simplification of the code. Tool choices should never be based on either of the following propositions:

  • "Microsoft said I should."
  • "Joe thought this looked cool."

Microsoft may or may not be right about whether a given tool is the right fit, and Joe may or may not be a good arbiter of what's hip. You should think carefully and make your own decisions about whether a certain tool brings you the most benefit for the costs, and implement accordingly.

Principle 5. Allow for Extension

It is an axiom of distributed programming that no matter how small your user base is when you start, if you write a good enough application, eventually you will have to scale up. Therefore, distributed applications must always be architected for scalability in the face of future need. Likewise, any application written well enough will someday be put to uses its authors never intended or imagined. Therefore, applications should be written in such a way as to allow for extension.

In general, this means writing code that allows for the unknown. This sounds like an impossible proposition, but in reality, the key factor is understanding where future users are likely to want to change the way your application works, and making those areas as loosely coupled to the rest of the application as possible.

Let's examine the notion of loose coupling first. Many developers think of loose coupling as using interfaces. That is, instead of:

Document document = new Document();


IDocument document = new Document();

The code is now bound to an abstract interface called IDocument instead of the concrete Document class. However, that call to new Document() is still tightly coupled. Changing it later is impossible without recompiling, which defeats the whole purpose of loose coupling. Instead, what is needed is a factory method. This is usually a static method on the class itself that can return new instances of a given type.

IDocument document = Document.CreateInstance("some_document_type",

The beauty of factory methods is that they can return any concrete type that implements IDocument, not just the original Document class, and they can look up the actual implementation at runtime using reflection. The implementation of CreateInstance might look like:

public static IDocument CreateInstance(string type, string asm)
    Assembly assembly = Assembly.Load(asm);
    return (Idocument)assembly.CreateInstance(type);

As long as the calling code has passed in an accessible assembly, and the type they asked for implements the IDocument interface, then it doesn't matter what the concrete type is, or even if it existed when the application was first written.

To allow future users to add or replace types this way, usually you will add a section to your application's .config file that contains references to the appropriate assemblies.

<?xml version="1.0" encoding="utf-8" ?>
        <section name="documents" 
type="System.Configuration.NameValueSectionHandler, System, 
Version=1.0.3300.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />

When allowing your users to choose a type of document to interact with, you simply grab the list of configured document types from the .config file and display them in some sort of selection interface. Once the user makes a decision, your factory method can load the assembly and type and return the appropriate implementation.

A special note about reflection: it gets a bum rap. Reflection is slower than direct coupling, but usually measured at about two or three times slower. That means you shouldn't do everything via reflection, but its judicious use for something like loading a dynamically assigned class at runtime (but binding it statically to a known interface) is perfectly reasonable, especially if you are talking about a modern distributed application, where the two more important criteria for boosting performance are limiting round trips on the network and minimizing hits to the database. If you can solve those problems, then maybe you can think about how a little reflection affects performance. I find that extensibility is usually a more important factor for me.


As you can see, there is plenty of needless complexity piling up all over the development landscape, and one of the principal tasks of any programmer is recognizing the bloat for what it is, and avoiding it where possible. .NET is no more immune to this problem than Java is. For that matter, as my good friend Ted Neward points out in his blog, ".NET is Microsoft's solution to the bloat build-up in COM." If we know anything about the technology industry, it's that history repeats itself. Programmers need to take it upon themselves to limit the bloat, and prune the complexity that is keeping their applications from living the good life. I hope these five principles give you a starting point for examining the choices and assumptions you have made about your projects, and give you some ideas of ways to make your programming life more simple and fun again.

Justin Gehtland is a programmer, author, mentor and instructor, focusing on real-world software applications.

In June 2004, O'Reilly Media, Inc., released Better, Faster, Lighter Java.

Return to ONJava.com.